With this we can have relative links for *everything*.
:py:`property_doc_contents` Property documentation contents
:py:`data_doc_contents` Data documentation contents
:py:`hooks_post_crawl` Hooks to call after the initial name crawl
+:py:`hooks_scope_enter` Hooks to call on scope enter
+:py:`hooks_scope_exit` Hooks to call on scope exit
:py:`hooks_docstring` Hooks to call when parsing a docstring
:py:`hooks_pre_page` Hooks to call before each page gets rendered
:py:`hooks_post_run` Hooks to call at the very end of the script run
added by the plugin *need* to have :py:`object` set to :py:`None` so the
script as well as other plugins can correctly distinguish them.
-Hooks listed in :py:`hooks_docstring` are called when docstrings are parsed.
-The first gets the raw docstring only processed by :py:`inspect.cleandoc()` and
-each following gets the output of the previous. When a hook returns an empty
-string, hooks later in the list are not called. String returned by the last
-hook is processed, if any, the same way as if no hooks would be present --- it
-gets partitioned into summary and content and those put to the output as-is,
-each paragraph wrapped in :html:`<p>` tags. The hooks are free to do anything
-with the docstring --- extracting metadata from it and returning it as-is,
-transpiling it from one markup language to another, or fully consuming it,
-populating the ``*_doc_contents`` variables mentioned above and returning
+The :py:`hooks_pre_scope` and :py:`hooks_post_scope` get called before entering
+and after leaving a name scope, and are meant mainly to aid with
+context-sensitive linking. Those scopes can be nested and can be called
+successively for the same scope --- for example, when rendering module docs,
+:py:`hooks_pre_scope` gets called first for the module scope, but then another
+:py:`hooks_pre_scope` gets called when rendering a summary for reference to an
+inner class. Then, :py:`hooks_post_scope` gets called in reverse order. The
+plugins are expected to implement a stack-like data structure for maintaining
+information about current scope. Both of those functions get passed the
+following arguments:
+
+.. class:: m-table
+
+=================== ===========================================================
+Keyword argument Content
+=================== ===========================================================
+:py:`type` Type of the scope that's being entered or exited. Same as
+ the enum passed to `custom URL formatters`_.
+:py:`path` Path of the module / class / function / enum / enum value /
+ data scope that's being entered or exited. A list of names,
+ :py:`'.'.join(path)` is equivalent to the fully qualified
+ name.
+:py:`param_names` In case of functions, list of parameter names. This
+ argument is not present otherwise.
+=================== ===========================================================
+
+Hooks listed in :py:`hooks_docstring` are called when docstrings are parsed,
+and always preceded by a corresponding :py:`hooks_pre_scope` call. The first
+listed hook gets the raw docstring only processed by :py:`inspect.cleandoc()`
+and each following gets the output of the previous. When a hook returns an
+empty string, hooks later in the list are not called. String returned by the
+last hook is processed, if any, the same way as if no hooks would be present
+--- it gets partitioned into summary and content and those put to the output
+as-is, each paragraph wrapped in :html:`<p>` tags. The hooks are free to do
+anything with the docstring --- extracting metadata from it and returning it
+as-is, transpiling it from one markup language to another, or fully consuming
+it, populating the ``*_doc_contents`` variables mentioned above and returning
nothing back. Each hook gets passed the following arguments:
.. class:: m-table
The :py:`hooks_pre_page` is called before each page of output gets rendered.
Can be used for example for resetting some internal counter for page-wide
-unique element IDs. It gets passed the following arguments:
-
-.. class:: m-table
-
-=================== ===========================================================
-Keyword argument Content
-=================== ===========================================================
-:py:`path` Path of the module/class/page to render. A list of names,
- for modules and classes :py:`'.'.join(path)` is equivalent
- to the fully qualified name. Useful to provide
- context-sensitive linking capabilities.
-=================== ===========================================================
-
-The :py:`hooks_post_run` is called after the whole run is done, useful for
-example to serialize cached internal state. Currently, this function get no
-arguments passed.
+unique element IDs. The :py:`hooks_post_run` is called after the whole run is
+done, useful for example to serialize cached internal state. Currently, those two functions get no arguments passed.
Registration function for a plugin that needs to query the :py:`OUTPUT` setting
might look like this --- the remaining keyword arguments will collapse into
self.hooks_post_crawl: List = []
self.hooks_docstring: List = []
+ self.hooks_pre_scope: List = []
+ self.hooks_post_scope: List = []
self.hooks_pre_page: List = []
self.hooks_post_run: List = []
def extract_module_doc(state: State, entry: Empty):
assert inspect.ismodule(entry.object)
+ # Call all scope enter hooks first
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path)
+
out = Empty()
out.url = entry.url
out.name = entry.path[-1]
out.summary = extract_docs(state, state.module_docs, entry.type, entry.path, entry.object.__doc__, summary_only=True)
+
+ # Call all scope exit hooks last
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path)
+
return out
def extract_class_doc(state: State, entry: Empty):
assert inspect.isclass(entry.object)
+ # Call all scope enter hooks first
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path)
+
out = Empty()
out.url = entry.url
out.name = entry.path[-1]
out.summary = extract_docs(state, state.class_docs, entry.type, entry.path, entry.object.__doc__, summary_only=True)
+
+ # Call all scope exit hooks last
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path)
+
return out
def extract_enum_doc(state: State, entry: Empty):
+ # Call all scope enter hooks first
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path)
+
out = Empty()
out.name = entry.path[-1]
out.id = state.config['ID_FORMATTER'](EntryType.ENUM, entry.path[-1:])
result.name = value.name
state.search += [result]
+ # Call all scope exit hooks last
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path)
+
return out
def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
positional_only = False
break
+ param_names = []
param_types = []
signature = []
for i, arg in enumerate(args):
name, type, type_link, default = arg
param = Empty()
param.name = name
+ param_names += [name]
# Don't include redundant type for the self argument
if i == 0 and name == 'self':
param.type, param.type_link = None, None
# thus name alone is not enough.
out.id = state.config['ID_FORMATTER'](EntryType.OVERLOADED_FUNCTION, entry.path[-1:] + param_types)
+ # Call all scope enter hooks for this particular overload
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path, param_names=param_names)
+
# Get summary and details. Passing the signature as well, so
# different overloads can (but don't need to) have different docs.
out.summary, out.content = extract_docs(state, state.function_docs, entry.type, entry.path, summary, signature='({})'.format(', '.join(signature)))
if out.content: out.has_details = True
+ # Call all scope exit hooks for this particular overload
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path, param_names=param_names)
+
overloads += [out]
# Sane introspection path for non-pybind11 code
out.id = state.config['ID_FORMATTER'](EntryType.FUNCTION, entry.path[-1:])
out.params = []
out.has_complex_params = False
- out.summary, out.content = extract_docs(state, state.function_docs, entry.type, entry.path, entry.object.__doc__)
- out.has_details = bool(out.content)
+ out.has_details = False
# Decide if classmethod or staticmethod in case this is a method
if inspect.isclass(parent):
out.type, out.type_link = extract_annotation(state, entry.path, type_hints['return'])
else:
out.type, out.type_link = extract_annotation(state, entry.path, signature.return_annotation)
+ param_names = []
for i in signature.parameters.values():
param = Empty()
param.name = i.name
+ param_names += [i.name]
if i.name in type_hints:
param.type, param.type_link = extract_annotation(state, entry.path, type_hints[i.name])
else:
param.type, param.type_link = None, None
out.params = [param]
out.type, out.type_link = None, None
+ param_names = []
+
+ # Call all scope enter hooks
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path, param_names=param_names)
+
+ # Get summary and details
+ # TODO: pass signature as well once @overload becomes a thing
+ out.summary, out.content = extract_docs(state, state.function_docs, entry.type, entry.path, entry.object.__doc__)
+ if out.content: out.has_details = True
+
+ # Call all scope exit hooks
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path, param_names=param_names)
overloads = [out]
path_str = '.'.join(entry.path)
for out in overloads:
signature = '({})'.format(', '.join(['{}: {}'.format(param.name, param.type) if param.type else param.name for param in out.params]))
+ param_names = [param.name for param in out.params]
+
+ # Call all scope enter hooks for this particular overload
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path, param_names=param_names)
# Get docs for each param and for the return value. Try this
# particular overload first, if not found then fall back to generic
out.return_value = ''
out.has_details = True
+ # Call all scope exit hooks for this particular overload
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path, param_names=param_names)
+
if not state.config['SEARCH_DISABLED']:
result = Empty()
result.flags = ResultFlag.from_type(ResultFlag.NONE, EntryType.FUNCTION)
out.type = None
return out
+ # Call all scope enter hooks before rendering the docs
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path)
+
out.is_gettable = entry.object.fget is not None
if entry.object.fget or (entry.object.fset and entry.object.__doc__):
docstring = entry.object.__doc__
out.is_deletable = entry.object.fdel is not None
out.has_details = bool(out.content)
+ # Call all scope exit hooks after rendering the docs
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path)
+
# For the type, if the property is gettable, get it from getters's return
# type. For write-only properties get it from setter's second argument
# annotation.
def extract_data_doc(state: State, parent, entry: Empty):
assert not inspect.ismodule(entry.object) and not inspect.isclass(entry.object) and not inspect.isroutine(entry.object) and not inspect.isframe(entry.object) and not inspect.istraceback(entry.object) and not inspect.iscode(entry.object)
+ # Call all scope enter hooks before rendering the docs
+ for hook in state.hooks_pre_scope:
+ hook(type=entry.type, path=entry.path)
+
out = Empty()
out.name = entry.path[-1]
out.id = state.config['ID_FORMATTER'](EntryType.DATA, entry.path[-1:])
out.summary, out.content = extract_docs(state, state.data_docs, entry.type, entry.path, '')
out.has_details = bool(out.content)
+ # Call all scope exit hooks after rendering the docs
+ for hook in state.hooks_post_scope:
+ hook(type=entry.type, path=entry.path)
+
# First try to get fully dereferenced type hints (with strings converted to
# actual annotations). If that fails (e.g. because a type doesn't exist),
# we'll take the non-dereferenced annotations instead.
f.write(b'\n')
def render_module(state: State, path, module, env):
+ # Call all scope enter hooks first
+ for hook in state.hooks_pre_scope:
+ hook(type=EntryType.MODULE, path=path)
+
# Generate breadcrumb as the first thing as it generates the output
# filename as a side effect
breadcrumb = []
logging.debug("generating %s", filename)
# Call all registered page begin hooks
- for hook in state.hooks_pre_page:
- hook(path=path)
+ for hook in state.hooks_pre_page: hook()
page = Empty()
page.summary, page.content = extract_docs(state, state.module_docs, EntryType.MODULE, path, module.__doc__)
render(state.config, 'module.html', page, env)
+ # Call all scope exit hooks last
+ for hook in state.hooks_post_scope:
+ hook(type=EntryType.MODULE, path=path)
+
def render_class(state: State, path, class_, env):
+ # Call all scope enter hooks first
+ for hook in state.hooks_pre_scope:
+ hook(type=EntryType.CLASS, path=path)
+
# Generate breadcrumb as the first thing as it generates the output
# filename as a side effect. It's a bit hairy because we need to figure out
# proper entry type for the URL formatter for each part of the breadcrumb.
logging.debug("generating %s", filename)
# Call all registered page begin hooks
- for hook in state.hooks_pre_page:
- hook(path=path)
+ for hook in state.hooks_pre_page: hook()
page = Empty()
page.summary, page.content = extract_docs(state, state.class_docs, EntryType.CLASS, path, class_.__doc__)
render(state.config, 'class.html', page, env)
+ # Call all scope exit hooks last
+ for hook in state.hooks_post_scope:
+ hook(type=EntryType.CLASS, path=path)
+
# Extracts image paths and transforms them to just the filenames
class ExtractImages(Transform):
# Max Docutils priority is 990, be sure that this is applied at the very
property_doc_contents=state.property_docs,
data_doc_contents=state.data_docs,
hooks_post_crawl=state.hooks_post_crawl,
+ hooks_pre_scope=state.hooks_pre_scope,
+ hooks_post_scope=state.hooks_post_scope,
hooks_docstring=state.hooks_docstring,
hooks_pre_page=state.hooks_pre_page,
hooks_post_run=state.hooks_post_run)
<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>
+ <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 <a class="m-doc" href="content.html">content</a>.</span>
<ul class="m-doc">
<li>module <a href="content.docstring_summary.html" class="m-doc">docstring_summary</a> <span class="m-doc">This module retains summary from the docstring</span></li>
<li>module <a href="content.submodule.html" class="m-doc">submodule</a> <span class="m-doc">This submodule has an external summary.</span></li>
- <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>
+ <li>class <a href="content.Class.html" class="m-doc">Class</a> <span class="m-doc">This overwrites the docstring for <a class="m-doc" href="content.Class.html">Class</a>.</span></li>
<li>class <a href="content.ClassWithSummary.html" class="m-doc">ClassWithSummary</a> <span class="m-doc">This class has summary from the docstring</span></li>
</ul>
</li>
<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 overwrites the docstring for <a class="m-doc" href="content.Class.html">Class</a>.</p>
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
<dt id="method">
<span class="m-doc-wrap-bumper">def <a href="#method" class="m-doc-self">method</a>(</span><span class="m-doc-wrap">self)</span>
</dt>
- <dd>This overwrites the docstring for <code>content.Class.method</code>, but
-doesn't add any detailed block.</dd>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.Class.html#method">method()</a>, but doesn't
+add any detailed block.</dd>
<dt>
<span class="m-doc-wrap-bumper">def <a href="#method_param_docs" class="m-doc">method_param_docs</a>(</span><span class="m-doc-wrap">self, a, b)</span>
</dt>
<dt id="a_property">
<a href="#a_property" class="m-doc-self">a_property</a> <span class="m-label m-flat m-warning">get</span>
</dt>
- <dd>This overwrites the docstring for <code>content.Class.a_property</code>,
-but doesn't add any detailed block.</dd>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.Class.html#a_property">a_property</a>, but doesn't
+add any detailed block.</dd>
<dt>
<a href="#a_property_with_details" class="m-doc">a_property_with_details</a> <span class="m-label m-flat m-warning">get</span>
</dt>
- <dd>This overwrites the docstring for <code>content.Class.a_property_with_details</code>.</dd>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.Class.html#a_property_with_details">a_property_with_details</a>.</dd>
<dt>
<a href="#annotated_property" class="m-doc">annotated_property</a>: float <span class="m-label m-flat m-warning">get</span>
</dt>
<h3>
content.<wbr />Class.<wbr /><a href="#a_property_with_details" class="m-doc-self">a_property_with_details</a> <span class="m-label m-flat m-warning">get</span>
</h3>
- <p>This overwrites the docstring for <code>content.Class.a_property_with_details</code>.</p>
+ <p>This overwrites the docstring for <a class="m-doc" href="content.Class.html#a_property_with_details">a_property_with_details</a>.</p>
<p>Detailed property docs.</p>
</div></section>
<section class="m-doc-details" id="annotated_property"><div>
<h3>
content.<wbr />Class.<wbr /><a href="#DATA_WITH_DETAILS" class="m-doc-self">DATA_WITH_DETAILS</a>: str
</h3>
-<p>Detailed docs for <code>data</code> in a class to check rendering.</p>
+<p>Detailed docs for <a class="m-doc" href="content.Class.html#DATA_WITH_DETAILS">DATA_WITH_DETAILS</a> in a class to check
+rendering.</p>
</div></section>
</section>
</div>
<h1>
content <span class="m-thin">module</span>
</h1>
- <p>This overwrites the docstring for <code>content</code>.</p>
+ <p>This overwrites the docstring for <a class="m-doc" href="content.html">content</a>.</p>
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
<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>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.Class.html">Class</a>.</dd>
<dt>class <a href="content.ClassWithSummary.html" class="m-doc">ClassWithSummary</a></dt>
<dd>This class has summary from the docstring</dd>
</dl>
<dt id="Enum">
<span class="m-doc-wrap-bumper">class <a href="#Enum" class="m-doc-self">Enum</a>(enum.Enum): </span><span class="m-doc-wrap"></span>
</dt>
- <dd>This overwrites the docstring for <code>content.Enum</code>, but
-doesn't add any detailed block.</dd>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.html#Enum">Enum</a>, but doesn't
+add any detailed block.</dd>
<dt>
<span class="m-doc-wrap-bumper">class <a href="#EnumWithSummary" class="m-doc">EnumWithSummary</a>(enum.Enum): </span><span class="m-doc-wrap"><a href="#EnumWithSummary-VALUE" class="m-doc">VALUE</a> = 0
<a href="#EnumWithSummary-ANOTHER" class="m-doc">ANOTHER</a> = 1</span>
<dt id="foo">
<span class="m-doc-wrap-bumper">def <a href="#foo" class="m-doc-self">foo</a>(</span><span class="m-doc-wrap">a, b)</span>
</dt>
- <dd>This overwrites the docstring for <code>content.foo</code>, but
-doesn't add any detailed block.</dd>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.html#foo">foo()</a>, but doesn't
+add any detailed block.</dd>
<dt>
<span class="m-doc-wrap-bumper">def <a href="#foo_with_details" class="m-doc">foo_with_details</a>(</span><span class="m-doc-wrap">a, b)</span>
</dt>
- <dd>This overwrites the docstring for <code>content.foo_with_details()</code>.</dd>
+ <dd>This overwrites the docstring for <a class="m-doc" href="content.html#foo_with_details">foo_with_details()</a>.</dd>
<dt>
<span class="m-doc-wrap-bumper">def <a href="#full_docstring" class="m-doc">full_docstring</a>(</span><span class="m-doc-wrap">a, b) -> str</span>
</dt>
<dt id="CONSTANT">
<a href="#CONSTANT" class="m-doc-self">CONSTANT</a>: float = 3.14
</dt>
- <dd>This is finally a docstring for <code>content.CONSTANT</code></dd>
+ <dd>This is finally a docstring for <a class="m-doc" href="content.html#CONSTANT">CONSTANT</a></dd>
<dt>
<a href="#DATA_WITH_DETAILS" class="m-doc">DATA_WITH_DETAILS</a>: str = 'heyoo'
</dt>
- <dd>This is finally a docstring for <code>content.DATA_WITH_DETAILS</code></dd>
+ <dd>This is finally a docstring for <a class="m-doc" href="content.html#DATA_WITH_DETAILS">DATA_WITH_DETAILS</a></dd>
<dt>
<a href="#DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE" class="m-doc">DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE</a> = None
</dt>
</tr>
</tbody>
</table>
-<p>And this is detailed docs added to the docstring summary.</p>
+<p>And this is detailed docs added to the docstring summary. <a class="m-doc" href="content.html#EnumWithSummary-VALUE">VALUE</a>!!</p>
</div></section>
</section>
<section>
<h3>
<span class="m-doc-wrap-bumper">def content.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#foo_with_details" class="m-doc-self">foo_with_details</a>(</span><span class="m-doc-wrap">a, b)</span></span>
</h3>
- <p>This overwrites the docstring for <code>content.foo_with_details()</code>.</p>
+ <p>This overwrites the docstring for <a class="m-doc" href="content.html#foo_with_details">foo_with_details()</a>.</p>
<div class="m-note m-info">
Detailed docs for this function</div>
</div></section>
<h3>
content.<wbr /><a href="#DATA_WITH_DETAILS" class="m-doc-self">DATA_WITH_DETAILS</a>: str
</h3>
- <p>This is finally a docstring for <code>content.DATA_WITH_DETAILS</code></p>
+ <p>This is finally a docstring for <a class="m-doc" href="content.html#DATA_WITH_DETAILS">DATA_WITH_DETAILS</a></p>
<p>Detailed docs for the data. <strong>YAY.</strong></p>
</div></section>
<section class="m-doc-details" id="DATA_WITH_DETAILS_BUT_NO_SUMMARY_NEITHER_TYPE"><div>
.. role:: label-info
:class: m-label m-info
+.. The actual correctness of relative references is tested in
+ inspect_type_links in order to test both absolute -> relative and
+ relative -> absolute direction at the same place. Here it's just verifying
+ that scopes are correctly propagated to all places where it matters.
+
.. py:module:: content
- :summary: This overwrites the docstring for ``content``.
+ :summary: This overwrites the docstring for :ref:`content`.
This is detailed module docs. I kinda *hate* how it needs to be indented,
tho.
:summary: This submodule has an external summary.
.. py:class:: content.Class
- :summary: This overwrites the docstring for ``content.Class``.
+ :summary: This overwrites the docstring for :ref:`Class`.
This is detailed class docs. Here I *also* hate how it needs to be
indented.
A dunder method shown in the detailed view.
.. py:function:: content.Class.method
- :summary: This overwrites the docstring for ``content.Class.method``, but
- doesn't add any detailed block.
+ :summary: This overwrites the docstring for :ref:`method()`, but doesn't
+ add any detailed block.
.. py:function:: content.Class.method_with_details
The ``self`` isn't documented and thus also not included in the list.
.. py:property:: content.Class.a_property
- :summary: This overwrites the docstring for ``content.Class.a_property``,
- but doesn't add any detailed block.
+ :summary: This overwrites the docstring for :ref:`a_property`, but doesn't
+ add any detailed block.
.. py:property:: content.Class.a_property_with_details
- :summary: This overwrites the docstring for ``content.Class.a_property_with_details``.
+ :summary: This overwrites the docstring for :ref:`a_property_with_details`.
Detailed property docs.
.. py:data:: content.Class.DATA_WITH_DETAILS
- Detailed docs for ``data`` in a class to check rendering.
+ Detailed docs for :ref:`DATA_WITH_DETAILS` in a class to check
+ rendering.
.. py:class:: content.ClassWithSummary
This class has external details but summary from the docstring.
.. py:enum:: content.Enum
- :summary: This overwrites the docstring for ``content.Enum``, but
- doesn't add any detailed block.
+ :summary: This overwrites the docstring for :ref:`Enum`, but doesn't
+ add any detailed block.
.. py:enum:: content.EnumWithSummary
- And this is detailed docs added to the docstring summary.
+ And this is detailed docs added to the docstring summary. :ref:`VALUE`!!
.. py:function:: content.foo
- :summary: This overwrites the docstring for ``content.foo``, but
- doesn't add any detailed block.
+ :summary: This overwrites the docstring for :ref:`foo()`, but doesn't
+ add any detailed block.
.. py:function:: content.foo_with_details
- :summary: This overwrites the docstring for ``content.foo_with_details()``.
+ :summary: This overwrites the docstring for :ref:`foo_with_details()`.
.. container:: m-note m-info
:param b: Second
.. py:data:: content.CONSTANT
- :summary: This is finally a docstring for ``content.CONSTANT``
+ :summary: This is finally a docstring for :ref:`CONSTANT`
.. py:data:: content.DATA_WITH_DETAILS
- :summary: This is finally a docstring for ``content.DATA_WITH_DETAILS``
+ :summary: This is finally a docstring for :ref:`DATA_WITH_DETAILS`
Detailed docs for the data. **YAY.**
<span class="m-breadcrumb"><a href="content_parse_docstrings.html">content_parse_docstrings</a>.<wbr/></span>Class <span class="m-thin">class</span>
</h1>
<p>This class has a <em>serious</em> docstring.
-With a multi-line summary.</p>
+With a multi-line summary. Relative reference to <a class="m-doc" href="content_parse_docstrings.Class.html#a_property">a_property</a> works
+even from a summary.</p>
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
<dt>
<a href="#a_property" class="m-doc">a_property</a>: float <span class="m-label m-flat m-warning">get</span>
</dt>
- <dd>This property has a <em>serious</em> docstring.</dd>
+ <dd>The <a class="m-doc" href="content_parse_docstrings.Class.html#a_property">a_property</a> has a <em>serious</em> docstring.</dd>
</dl>
</section>
<section>
<h3>
content_parse_docstrings.<wbr />Class.<wbr /><a href="#a_property" class="m-doc-self">a_property</a>: float <span class="m-label m-flat m-warning">get</span>
</h3>
- <p>This property has a <em>serious</em> docstring.</p>
+ <p>The <a class="m-doc" href="content_parse_docstrings.Class.html#a_property">a_property</a> has a <em>serious</em> docstring.</p>
<p>And property <strong>details</strong> as well.</p>
</div></section>
</section>
<h1>
content_parse_docstrings <span class="m-thin">module</span>
</h1>
- <p>This module has a <em>serious</em> docstring.</p>
+ <p>This module has a <em>serious</em> docstring. And a <a class="m-doc" href="content_parse_docstrings.Class.html">Class</a>.</p>
<div class="m-block m-default">
<h3>Contents</h3>
<ul>
<dl class="m-doc">
<dt>class <a href="content_parse_docstrings.Class.html" class="m-doc">Class</a></dt>
<dd>This class has a <em>serious</em> docstring.
-With a multi-line summary.</dd>
+With a multi-line summary. Relative reference to <a class="m-doc" href="content_parse_docstrings.Class.html#a_property">a_property</a> works
+even from a summary.</dd>
</dl>
</section>
<section id="enums">
<dt>
<span class="m-doc-wrap-bumper">class <a href="#Enum" class="m-doc">Enum</a>(enum.Enum): </span><span class="m-doc-wrap"><a href="#Enum-VALUE" class="m-doc">VALUE</a> = 3</span>
</dt>
- <dd>This enum has a <em>serious</em> docstring.</dd>
+ <dd>This enum has a <em>serious</em> docstring. <a class="m-doc" href="content_parse_docstrings.html#Enum-VALUE">VALUE</a> works from a summary.</dd>
</dl>
</section>
<section id="functions">
<span class="m-doc-wrap-bumper">def <a href="#function" class="m-doc">function</a>(</span><span class="m-doc-wrap">a: str,
b: int) -> float</span>
</dt>
- <dd>This function has a <em>serious</em> docstring.</dd>
+ <dd>This <a class="m-doc" href="content_parse_docstrings.html#function">function()</a> has a <em>serious</em> docstring.</dd>
<dt id="summary_only">
<span class="m-doc-wrap-bumper">def <a href="#summary_only" class="m-doc-self">summary_only</a>(</span><span class="m-doc-wrap">)</span>
</dt>
<h3>
class content_parse_docstrings.<wbr /><a href="#Enum" class="m-doc-self">Enum</a>(enum.Enum)
</h3>
- <p>This enum has a <em>serious</em> docstring.</p>
+ <p>This enum has a <em>serious</em> docstring. <a class="m-doc" href="content_parse_docstrings.html#Enum-VALUE">VALUE</a> works from a summary.</p>
<table class="m-table m-fullwidth m-flat m-doc">
<thead><tr><th style="width: 1%">Enumerators</th><th></th></tr></thead>
<tbody>
<span class="m-doc-wrap-bumper">def content_parse_docstrings.<wbr /></span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#function" class="m-doc-self">function</a>(</span><span class="m-doc-wrap">a: str,
b: int) -> float</span></span>
</h3>
- <p>This function has a <em>serious</em> docstring.</p>
+ <p>This <a class="m-doc" href="content_parse_docstrings.html#function">function()</a> has a <em>serious</em> docstring.</p>
<table class="m-table m-fullwidth m-flat">
<thead>
<tr><th colspan="2">Parameters</th></tr>
<tbody>
<tr>
<td style="width: 1%">a</td>
- <td>And parameter docs.
+ <td>And parameter docs, referring to <a class="m-doc" href="content_parse_docstrings.html#function">function()</a> as well.
On multiple lines.</td>
</tr>
<tr>
<tfoot>
<tr>
<th>Returns</th>
- <td>This too.</td>
+ <td>This too. In the <a class="m-doc" href="content_parse_docstrings.html#function">function()</a>.</td>
</tr>
</tfoot>
</table>
-"""This module has a *serious* docstring.
+"""This module has a *serious* docstring. And a :ref:`Class`.
And module **details** as well."""
class Class:
"""This class has a *serious* docstring.
- With a multi-line summary.
+ With a multi-line summary. Relative reference to :ref:`a_property` works
+ even from a summary.
And class **details** as well."""
@property
def a_property(self) -> float:
- """This property has a *serious* docstring.
+ """The :ref:`a_property` has a *serious* docstring.
And property **details** as well."""
class Enum(enum.Enum):
- """This enum has a *serious* docstring.
+ """This enum has a *serious* docstring. :ref:`VALUE` works from a summary.
And property **details** as well."""
Enum.VALUE.__doc__ = "Tho enum value docs are unfortunately *not* processed."
def function(a: str, b: int) -> float:
- """This function has a *serious* docstring.
+ """This :ref:`function()` has a *serious* docstring.
- :param a: And parameter docs.
+ :param a: And parameter docs, referring to :ref:`function()` as well.
On multiple lines.
:param b: *Wow.*
- :return: This too.
+ :return: This too. In the :ref:`function()`.
And details.
**Amazing**."""
return [node]
post_crawl_call_count = 0
+scope_stack = []
docstring_call_count = 0
pre_page_call_count = 0
post_run_call_count = 0
global post_crawl_call_count
post_crawl_call_count = post_crawl_call_count + 1
+def _pre_scope(type, path, **kwargs):
+ global scope_stack
+ scope_stack += [(type, path)]
+
+def _post_scope(type, path, **kwargs):
+ global scope_stack
+ assert scope_stack[-1] == (type, path)
+ scope_stack = scope_stack[:-1]
+
def _docstring(**kwargs):
docstring_call_count += 1
global post_run_call_count
post_run_call_count = post_run_call_count + 1
-def register_mcss(hooks_post_crawl, hooks_docstring, hooks_pre_page, hooks_post_run, **kwargs):
+def register_mcss(hooks_post_crawl, hooks_pre_scope, hooks_post_scope, hooks_docstring, hooks_pre_page, hooks_post_run, **kwargs):
hooks_post_crawl += [_post_crawl]
+ hooks_pre_scope += [_pre_scope]
+ hooks_post_scope += [_post_scope]
hooks_docstring += [_docstring]
hooks_pre_page += [_pre_page]
hooks_post_run += [_post_run]
import fancyline
self.assertEqual(fancyline.post_crawl_call_count, 1)
+ self.assertEqual(fancyline.scope_stack, [])
+
# No code, thus no docstrings processed
self.assertEqual(fancyline.docstring_call_count, 0)
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..'))
import m.htmlsanity
-referer_path = []
+# All those initialized in register() or register_mcss()
+current_referer_path = None
module_doc_output = None
class_doc_output = None
enum_doc_output = None
# Add prefixes of the referer path to the global prefix list, iterate
# through all of them, with names "closest" to the referer having a
# priority and try to find the name
- global intersphinx_inventory, intersphinx_name_prefixes
+ global current_referer_path, intersphinx_inventory, intersphinx_name_prefixes
+ referer_path = current_referer_path[-1] if current_referer_path else []
prefixes = ['.'.join(referer_path[:len(referer_path) - i]) + '.' for i, _ in enumerate(referer_path)] + intersphinx_name_prefixes
for prefix in prefixes:
found = None
node = nodes.literal(rawtext, target, **_options)
return [node], []
+def scope_enter(path, **kwargs):
+ global current_referer_path
+ current_referer_path += [path]
+
+def scope_exit(path, **kwargs):
+ global current_referer_path
+ assert current_referer_path[-1] == path, "%s %s" % (current_referer_path, path)
+ current_referer_path = current_referer_path[:-1]
+
+def check_scope_stack_empty(**kwargs):
+ global current_referer_path
+ assert not current_referer_path
+
def consume_docstring(type, path: List[str], signature: Optional[str], doc: str) -> str:
# Create the directive header based on type
if type.name == 'MODULE':
if doc_output.get('summary') is None: doc_output['summary'] = ''
if doc_output.get('content') is None: doc_output['content'] = ''
-def remember_referer_path(path):
- global referer_path
- referer_path = path
-
def merge_inventories(name_map, **kwargs):
global intersphinx_inventory
f.write(compressor.compress('{} {} 2 {} {}\n'.format(path, type_, url, title).encode('utf-8')))
f.write(compressor.flush())
-def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_doc_contents, function_doc_contents, property_doc_contents, data_doc_contents, hooks_post_crawl, hooks_docstring, hooks_pre_page, **kwargs):
- global module_doc_output, class_doc_output, enum_doc_output, function_doc_output, property_doc_output, data_doc_output, inventory_filename
+def register_mcss(mcss_settings, module_doc_contents, class_doc_contents, enum_doc_contents, function_doc_contents, property_doc_contents, data_doc_contents, hooks_post_crawl, hooks_pre_scope, hooks_post_scope, hooks_docstring, hooks_post_run, **kwargs):
+ global current_referer_path, module_doc_output, class_doc_output, enum_doc_output, function_doc_output, property_doc_output, data_doc_output, inventory_filename
+ current_referer_path = []
module_doc_output = module_doc_contents
class_doc_output = class_doc_contents
enum_doc_output = enum_doc_contents
rst.roles.register_local_role('ref', ref)
+ hooks_pre_scope += [scope_enter]
+ hooks_post_scope += [scope_exit]
if mcss_settings.get('M_SPHINX_PARSE_DOCSTRINGS', False):
hooks_docstring += [consume_docstring]
- hooks_pre_page += [remember_referer_path]
hooks_post_crawl += [merge_inventories]
+ # Just a sanity check
+ hooks_post_run += [check_scope_stack_empty]
def _pelican_configure(pelicanobj):
# For backwards compatibility, the input directory is pelican's CWD