Skip to content

Commit

Permalink
add support for rST field lists, refs mitmproxy#275
Browse files Browse the repository at this point in the history
  • Loading branch information
mhils committed Mar 30, 2022
1 parent ae2aaba commit 336b398
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# Unreleased: pdoc next

- Add support for reStructuredText field lists: `:param foo: text`.
([#275](https://github.com/mitmproxy/pdoc/issues/275), [@mhils](https://github.com/mhils))

# 2022-03-23: pdoc 10.0.4

Expand Down
4 changes: 2 additions & 2 deletions pdoc/doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ def own_members(self) -> list[Doc]:

@cached_property
def members(self) -> dict[str, Doc]:
"""A mapping from all members to their documentation objects.
"""A mapping from all members to their documentation objects.
This mapping includes private members; they are only filtered out as part of the template logic.
"""
Expand Down
66 changes: 63 additions & 3 deletions pdoc/docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,11 @@ def numpy(docstring: str) -> str:
"Warns",
"Attributes",
):
contents += f"###### {heading}\n" f"{_numpy_parameters(content)}"
contents += f"###### {heading}\n{_numpy_parameters(content)}"
elif heading == "See Also":
contents += f"###### {heading}\n" f"{_numpy_seealso(content)}"
contents += f"###### {heading}\n{_numpy_seealso(content)}"
else:
contents += f"###### {heading}\n" f"{dedent(content)}"
contents += f"###### {heading}\n{dedent(content)}"
contents += tail
return contents

Expand Down Expand Up @@ -219,6 +219,8 @@ def rst(contents: str, source_file: Path | None) -> str:

contents = _rst_footnotes(contents)

contents = _rst_fields(contents)

return contents


Expand Down Expand Up @@ -362,3 +364,61 @@ def _rst_admonition(m: re.Match[str]) -> str:
contents,
flags=re.MULTILINE | re.VERBOSE,
)


def _rst_fields(contents: str) -> str:
"""
Convert reStructuredText fields to Markdown.
<https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#rst-field-lists>
"""

_has_parameter_section = False
_has_raises_section = False

def _rst_field(m: re.Match[str]) -> str:
type = m["type"]
body = m["body"].strip()

if m["name"]:
name = f"**{m['name'].strip()}**: "
else:
name = ""

if type == "param":
nonlocal _has_parameter_section
text = f" - {name}{body}\n"
if not _has_parameter_section:
_has_parameter_section = True
text = "\n###### Parameters\n" + text
return text
elif type == "type":
return "" # we expect users to use modern type annotations.
elif type == "return":
body = indent(body, "> ", lambda line: True)
return f"\n###### Returns\n{body}"
elif type == "rtype":
return "" # we expect users to use modern type annotations.
elif type == "raises":
nonlocal _has_raises_section
text = f" - {name}{body}\n"
if not _has_raises_section:
_has_raises_section = True
text = "\n###### Raises\n" + text
return text
else:
return m[0]

field = "param|type|return|rtype|raises"
return re.sub(
rf"""
^:(?P<type>{field})(?:[ ]+(?P<name>.+))?:
(?P<body>.*(
\n # empty lines
| # or
[ ]+.+ # lines with indentation
)*(?:\n|$))
""",
_rst_field,
contents,
flags=re.MULTILINE | re.VERBOSE,
)
149 changes: 146 additions & 3 deletions test/testdata/flavors_rst.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ <h2>API Documentation</h2>
<li>
<a class="function" href="#include">include</a>
</li>
<li>
<a class="function" href="#fields">fields</a>
</li>
<li>
<a class="function" href="#fields_invalid">fields_invalid</a>
</li>
<li>
<a class="function" href="#fields_exception">fields_exception</a>
</li>
</ul>


Expand Down Expand Up @@ -190,7 +199,7 @@ <h1 class="modulename">

<span class="k">def</span> <span class="nf">footnote4</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> There is not footnote for this reference [#]_.</span>
<span class="sd"> There is no footnote for this reference [#]_.</span>
<span class="sd"> &quot;&quot;&quot;</span>


Expand All @@ -200,6 +209,34 @@ <h1 class="modulename">

<span class="sd"> .. include:: flavors_rst_include/include.rst</span>
<span class="sd"> &quot;&quot;&quot;</span>


<span class="k">def</span> <span class="nf">fields</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">bar</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;This method has field descriptions.</span>

<span class="sd"> :param foo: A list of strings,</span>
<span class="sd"> defaults to None</span>
<span class="sd"> :type foo: list, optional</span>
<span class="sd"> :param bar: Another</span>
<span class="sd"> boolean.</span>
<span class="sd"> :return: Another list of strs,</span>
<span class="sd"> or maybe `None`.</span>
<span class="sd"> :rtype: A list of strings.</span>
<span class="sd"> &quot;&quot;&quot;</span>


<span class="k">def</span> <span class="nf">fields_invalid</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;This method has invalid `:param` definitions.</span>

<span class="sd"> :param: What is this for?</span>
<span class="sd"> &quot;&quot;&quot;</span>


<span class="k">def</span> <span class="nf">fields_exception</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> :raises RuntimeError: Some multi-line</span>
<span class="sd"> exception description.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>

</details>
Expand Down Expand Up @@ -574,13 +611,13 @@ <h1 class="modulename">
<summary>View Source</summary>
<div class="pdoc-code codehilite"><pre><span></span><span class="k">def</span> <span class="nf">footnote4</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> There is not footnote for this reference [#]_.</span>
<span class="sd"> There is no footnote for this reference [#]_.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>

</details>

<div class="docstring"><p>There is not footnote for this reference [#]_.</p>
<div class="docstring"><p>There is no footnote for this reference [#]_.</p>
</div>


Expand Down Expand Up @@ -615,6 +652,112 @@ <h1 class="modulename">
</div>


</section>
<section id="fields">
<div class="attr function"><a class="headerlink" href="#fields">#&nbsp;&nbsp</a>


<span class="def">def</span>
<span class="name">fields</span><span class="signature">(foo: list[str] = None, bar: bool = True) -&gt; list[str]</span>:
</div>

<details>
<summary>View Source</summary>
<div class="pdoc-code codehilite"><pre><span></span><span class="k">def</span> <span class="nf">fields</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">bar</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;This method has field descriptions.</span>

<span class="sd"> :param foo: A list of strings,</span>
<span class="sd"> defaults to None</span>
<span class="sd"> :type foo: list, optional</span>
<span class="sd"> :param bar: Another</span>
<span class="sd"> boolean.</span>
<span class="sd"> :return: Another list of strs,</span>
<span class="sd"> or maybe `None`.</span>
<span class="sd"> :rtype: A list of strings.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>

</details>

<div class="docstring"><p>This method has field descriptions.</p>

<h6 id="parameters">Parameters</h6>

<ul>
<li><strong>foo</strong>: A list of strings,
defaults to None</li>
<li><strong>bar</strong>: Another
boolean.</li>
</ul>

<h6 id="returns">Returns</h6>

<blockquote>
<p>Another list of strs,
or maybe <code>None</code>.</p>
</blockquote>
</div>


</section>
<section id="fields_invalid">
<div class="attr function"><a class="headerlink" href="#fields_invalid">#&nbsp;&nbsp</a>


<span class="def">def</span>
<span class="name">fields_invalid</span><span class="signature">(foo: list[str] = None) -&gt; list[str]</span>:
</div>

<details>
<summary>View Source</summary>
<div class="pdoc-code codehilite"><pre><span></span><span class="k">def</span> <span class="nf">fields_invalid</span><span class="p">(</span><span class="n">foo</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
<span class="sd">&quot;&quot;&quot;This method has invalid `:param` definitions.</span>

<span class="sd"> :param: What is this for?</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>

</details>

<div class="docstring"><p>This method has invalid <code>:param</code> definitions.</p>

<h6 id="parameters">Parameters</h6>

<ul>
<li>What is this for?</li>
</ul>
</div>


</section>
<section id="fields_exception">
<div class="attr function"><a class="headerlink" href="#fields_exception">#&nbsp;&nbsp</a>


<span class="def">def</span>
<span class="name">fields_exception</span><span class="signature">()</span>:
</div>

<details>
<summary>View Source</summary>
<div class="pdoc-code codehilite"><pre><span></span><span class="k">def</span> <span class="nf">fields_exception</span><span class="p">():</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> :raises RuntimeError: Some multi-line</span>
<span class="sd"> exception description.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>

</details>

<div class="docstring"><h6 id="raises">Raises</h6>

<ul>
<li><strong>RuntimeError</strong>: Some multi-line
exception description.</li>
</ul>
</div>


</section>
</main>
</body>
Expand Down
30 changes: 29 additions & 1 deletion test/testdata/flavors_rst.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def footnote3():

def footnote4():
"""
There is not footnote for this reference [#]_.
There is no footnote for this reference [#]_.
"""


Expand All @@ -123,3 +123,31 @@ def include():
.. include:: flavors_rst_include/include.rst
"""


def fields(foo: list[str] = None, bar: bool = True) -> list[str]:
"""This method has field descriptions.
:param foo: A list of strings,
defaults to None
:type foo: list, optional
:param bar: Another
boolean.
:return: Another list of strs,
or maybe `None`.
:rtype: A list of strings.
"""


def fields_invalid(foo: list[str] = None) -> list[str]:
"""This method has invalid `:param` definitions.
:param: What is this for?
"""


def fields_exception():
"""
:raises RuntimeError: Some multi-line
exception description.
"""
7 changes: 5 additions & 2 deletions test/testdata/flavors_rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@
<function def footnote1(): ... # Cite the relevant li…>
<function def footnote2(): ... # Autonumbered footnot…>
<function def footnote3(): ... # Auto-symbol footnote…>
<function def footnote4(): ... # There is not footnot…>
<function def include(): ... # Included from anothe…>>
<function def footnote4(): ... # There is no footnote…>
<function def include(): ... # Included from anothe…>
<function def fields(foo: list[str] = None, bar: bool = True) -> list[str]: ... # This method has fiel…>
<function def fields_invalid(foo: list[str] = None) -> list[str]: ... # This method has inva…>
<function def fields_exception(): ... # :raises RuntimeError…>>

0 comments on commit 336b398

Please sign in to comment.