Skip to content

Commit

Permalink
Link partially qualified identifiers when documenting single module (#…
Browse files Browse the repository at this point in the history
…544)

* Link partially qualified names when all modules are part of the same package

This is particularly useful when documenting a single package with an inconveniently long name

* Add test for lesser qualified links

* Update test snapshots

* [autofix.ci] apply automated fixes

* Prefix partially qualified identifiers with "."

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes

* refactor: handle relative links

* [autofix.ci] apply automated fixes

* nits

* be more consistent about link names

* [autofix.ci] apply automated fixes

* fix relative links

* Fix relative links to parent modules not working in submodules

* regex nits: allow more than two dots, but only at the start

* resolve relative references in the context of the module they were taken from

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
  • Loading branch information
3 people authored Sep 10, 2023
1 parent 1062e18 commit c49da24
Show file tree
Hide file tree
Showing 21 changed files with 724 additions and 56 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

- Add compatibility with Python 3.12
([#620](https://github.com/mitmproxy/pdoc/pull/620), @mhils)
- Add support for relative links. Instead of explicitly referring to `mypackage.helpers.foo`,
one can now also refer to `.helpers.foo` within the `mypackage` module, or `..helpers.foo` in a submodule.
([#544](https://github.com/mitmproxy/pdoc/pull/544), @Crozzers)
- Function signatures will now display "Foo" instead "demo.Foo" if the function is in the same module.
([#544](https://github.com/mitmproxy/pdoc/pull/544), @mhils)
- pdoc now also picks up docstrings from `.pyi` stub files.
([#619](https://github.com/mitmproxy/pdoc/pull/619), @mhils)
- Fix horizontal scroll navigation z-index issue.
Expand Down
55 changes: 40 additions & 15 deletions pdoc/render_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@
anchorlinenos=True,
)
"""
The pygments formatter used for pdoc.render_helpers.highlight.
The pygments formatter used for pdoc.render_helpers.highlight.
Overwrite this to configure pygments highlighting of code blocks.
The usage of the `.codehilite` CSS selector in custom templates is deprecated since pdoc 10, use `.pdoc-code` instead.
"""

signature_formatter = pygments.formatters.HtmlFormatter(nowrap=True)
"""
The pygments formatter used for pdoc.render_helpers.format_signature.
The pygments formatter used for pdoc.render_helpers.format_signature.
Overwrite this to configure pygments highlighting of signatures.
"""

Expand Down Expand Up @@ -287,13 +287,30 @@ def linkify_repl(m: re.Match):
'</span><span class="o">.</span><span class="n">', "."
)
identifier = removesuffix(plain_text, "()")

# Check if this is a local reference within this module?
mod: pdoc.doc.Module = context["module"]
for qualname in qualname_candidates(identifier, namespace):
doc = mod.get(qualname)
if doc and context["is_public"](doc).strip():
return f'<a href="#{qualname}">{plain_text}</a>'

# Check if this is a relative reference?
if identifier.startswith("."):
taken_from_mod = mod
if namespace and (ns := mod.get(namespace)):
# Imported from somewhere else, so the relative reference should be from the original module.
taken_from_mod = context["all_modules"].get(ns.taken_from[0], mod)
if taken_from_mod.is_package:
# If we are in __init__.py, we want `.foo` to refer to a child module.
parent_module = taken_from_mod.modulename
else:
# If we are in a leaf module, we want `.foo` to refer to the adjacent module.
parent_module = taken_from_mod.modulename.rpartition(".")[0]
while identifier.startswith(".."):
identifier = identifier[1:]
parent_module = parent_module.rpartition(".")[0]
identifier = parent_module + identifier
else:
# Check if this is a local reference within this module?
for qualname in qualname_candidates(identifier, namespace):
doc = mod.get(qualname)
if doc and context["is_public"](doc).strip():
return f'<a href="#{qualname}">{plain_text}</a>'

module = ""
qualname = ""
Expand All @@ -309,9 +326,9 @@ def linkify_repl(m: re.Match):
and context["is_public"](doc).strip()
):
if plain_text.endswith("()"):
plain_text = f"{doc.fullname}()"
plain_text = f"{doc.qualname}()"
else:
plain_text = doc.fullname
plain_text = doc.qualname
return f'<a href="#{qualname}">{plain_text}</a>'
except ValueError:
# possible_sources did not find a parent module.
Expand All @@ -326,8 +343,13 @@ def linkify_repl(m: re.Match):
doc is not None and context["is_public"](doc).strip()
)
if target_exists_and_public:
assert doc is not None # mypy
if qualname:
qualname = f"#{qualname}"
if plain_text.endswith("()"):
plain_text = f"{doc.fullname}()"
else:
plain_text = doc.fullname
return f'<a href="{relative_link(context["module"].modulename, module)}{qualname}">{plain_text}</a>'
else:
return text
Expand All @@ -337,11 +359,14 @@ def linkify_repl(m: re.Match):
r"""
# Part 1: foo.bar or foo.bar() (without backticks)
(?<![/=?#&]) # heuristic: not part of a URL
\b
# First part of the identifier (e.g. "foo")
(?!\d)[a-zA-Z0-9_]+
# Rest of the identifier (e.g. ".bar")
# First part of the identifier (e.g. "foo") - this is optional for relative references.
(?:
\b
(?!\d)[a-zA-Z0-9_]+
|
\.* # We may also start with multiple dots.
)
# Rest of the identifier (e.g. ".bar" or "..bar")
(?:
# A single dot or a dot surrounded with pygments highlighting.
(?:\.|</span><span\ class="o">\.</span><span\ class="n">)
Expand Down
Empty file modified test/syntax_err/syntax_err.py
100644 → 100755
Empty file.
1 change: 1 addition & 0 deletions test/test_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def test_walk_specs():
"demopackage._child_e",
"demopackage.child_b",
"demopackage.child_c",
"demopackage.subpackage",
]
with pytest.raises(ValueError, match="No modules found matching spec: unknown"):
with pytest.warns(UserWarning, match="Cannot find spec for unknown"):
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ <h1 class="modulename">
</div>
<div id="Dog.friends" class="classattr">
<div class="attr variable">
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">demo.Dog</a>]</span>
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">Dog</a>]</span>


</div>
Expand Down
6 changes: 3 additions & 3 deletions test/testdata/demo_long.html
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ <h1 id="a-second-section">A Second Section</h1>
</section>
<section id="FOO_SINGLETON">
<div class="attr variable">
<span class="name">FOO_SINGLETON</span><span class="annotation">: <a href="#Foo">demo_long.Foo</a></span>
<span class="name">FOO_SINGLETON</span><span class="annotation">: <a href="#Foo">Foo</a></span>


</div>
Expand Down Expand Up @@ -595,7 +595,7 @@ <h1 id="a-second-section">A Second Section</h1>
<div class="attr function">

<span class="def">def</span>
<span class="name">a_complex_function</span><span class="signature pdoc-code multiline">(<span class="param"> <span class="n">a</span><span class="p">:</span> <span class="nb">str</span>,</span><span class="param"> <span class="n">b</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n"><a href="#Foo">demo_long.Foo</a></span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>,</span><span class="param"> <span class="o">*</span>,</span><span class="param"> <span class="n">c</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="o">~</span><span class="n">T</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span></span><span class="return-annotation">) -> <span class="n">Optional</span><span class="p">[</span><span class="o">~</span><span class="n">T</span><span class="p">]</span>:</span></span>
<span class="name">a_complex_function</span><span class="signature pdoc-code multiline">(<span class="param"> <span class="n">a</span><span class="p">:</span> <span class="nb">str</span>,</span><span class="param"> <span class="n">b</span><span class="p">:</span> <span class="n">Union</span><span class="p">[</span><span class="n"><a href="#Foo">Foo</a></span><span class="p">,</span> <span class="nb">str</span><span class="p">]</span>,</span><span class="param"> <span class="o">*</span>,</span><span class="param"> <span class="n">c</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="o">~</span><span class="n">T</span><span class="p">]</span> <span class="o">=</span> <span class="kc">None</span></span><span class="return-annotation">) -> <span class="n">Optional</span><span class="p">[</span><span class="o">~</span><span class="n">T</span><span class="p">]</span>:</span></span>

<label class="view-source-button" for="a_complex_function-view-source"><span>View Source</span></label>

Expand Down Expand Up @@ -782,7 +782,7 @@ <h1 id="a-second-section">A Second Section</h1>
<div class="attr function">

<span class="def">def</span>
<span class="name">a_regular_function</span><span class="signature pdoc-code condensed">(<span class="param"><span class="bp">self</span></span><span class="return-annotation">) -> <span class="n"><a href="#Foo">demo_long.Foo</a></span>:</span></span>
<span class="name">a_regular_function</span><span class="signature pdoc-code condensed">(<span class="param"><span class="bp">self</span></span><span class="return-annotation">) -> <span class="n"><a href="#Foo">Foo</a></span>:</span></span>

<label class="view-source-button" for="Foo.a_regular_function-view-source"><span>View Source</span></label>

Expand Down
16 changes: 9 additions & 7 deletions test/testdata/demopackage.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ <h2>Submodules</h2>
<li><a href="demopackage/child_b.html">child_b</a></li>
<li><a href="demopackage/child_c.html">child_c</a></li>
<li><a href="demopackage/_child_e.html">_child_e</a></li>
<li><a href="demopackage/subpackage.html">subpackage</a></li>
</ul>

<h2>API Documentation</h2>
Expand Down Expand Up @@ -77,14 +78,14 @@ <h2>API Documentation</h2>
<h1 class="modulename">
demopackage </h1>

<div class="docstring"><p>A test package</p>
<div class="docstring"><p>A test package with a sub-package at <code><a href="demopackage/subpackage.html">demopackage.subpackage</a></code>.</p>
</div>

<input id="mod-demopackage-view-source" class="view-source-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">

<label class="view-source-button" for="mod-demopackage-view-source"><span>View Source</span></label>

<div class="pdoc-code codehilite"><pre><span></span><span id="L-1"><a href="#L-1"><span class="linenos"> 1</span></a><span class="sd">&quot;&quot;&quot;A test package&quot;&quot;&quot;</span>
<div class="pdoc-code codehilite"><pre><span></span><span id="L-1"><a href="#L-1"><span class="linenos"> 1</span></a><span class="sd">&quot;&quot;&quot;A test package with a sub-package at `.subpackage`.&quot;&quot;&quot;</span>
</span><span id="L-2"><a href="#L-2"><span class="linenos"> 2</span></a><span class="kn">import</span> <span class="nn">demopackage2</span>
</span><span id="L-3"><a href="#L-3"><span class="linenos"> 3</span></a>
</span><span id="L-4"><a href="#L-4"><span class="linenos"> 4</span></a><span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">_child_e</span>
Expand All @@ -105,7 +106,8 @@ <h1 class="modulename">
</span><span id="L-19"><a href="#L-19"><span class="linenos">19</span></a> <span class="s2">&quot;demopackage2&quot;</span><span class="p">,</span>
</span><span id="L-20"><a href="#L-20"><span class="linenos">20</span></a> <span class="s2">&quot;_child_e&quot;</span><span class="p">,</span>
</span><span id="L-21"><a href="#L-21"><span class="linenos">21</span></a> <span class="s2">&quot;child_excluded&quot;</span><span class="p">,</span>
</span><span id="L-22"><a href="#L-22"><span class="linenos">22</span></a><span class="p">]</span>
</span><span id="L-22"><a href="#L-22"><span class="linenos">22</span></a> <span class="s2">&quot;subpackage&quot;</span><span class="p">,</span>
</span><span id="L-23"><a href="#L-23"><span class="linenos">23</span></a><span class="p">]</span>
</span></pre></div>


Expand Down Expand Up @@ -167,7 +169,7 @@ <h1 class="modulename">
</div>
<a class="headerlink" href="#B"></a>
<div class="pdoc-code codehilite"><pre><span></span><span id="B-8"><a href="#B-8"><span class="linenos"> 8</span></a><span class="k">class</span> <span class="nc">B</span><span class="p">:</span>
</span><span id="B-9"><a href="#B-9"><span class="linenos"> 9</span></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;This class is defined in .child_b.&quot;&quot;&quot;</span>
</span><span id="B-9"><a href="#B-9"><span class="linenos"> 9</span></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;This class is defined in .child_b. It has a B.b method.&quot;&quot;&quot;</span>
</span><span id="B-10"><a href="#B-10"><span class="linenos">10</span></a>
</span><span id="B-11"><a href="#B-11"><span class="linenos">11</span></a> <span class="n">b_type</span><span class="p">:</span> <span class="n">typing</span><span class="o">.</span><span class="n">Type</span><span class="p">[</span><span class="n">B</span><span class="p">]</span>
</span><span id="B-12"><a href="#B-12"><span class="linenos">12</span></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;we have a self-referential attribute here&quot;&quot;&quot;</span>
Expand All @@ -177,13 +179,13 @@ <h1 class="modulename">
</span></pre></div>


<div class="docstring"><p>This class is defined in .child_b.</p>
<div class="docstring"><p>This class is defined in <a href="demopackage/child_b.html">demopackage.child_b</a>. It has a <a href="#B.b">B.b</a> method.</p>
</div>


<div id="B.b_type" class="classattr">
<div class="attr variable">
<span class="name">b_type</span><span class="annotation">: Type[<a href="#B">demopackage.B</a>]</span>
<span class="name">b_type</span><span class="annotation">: Type[<a href="#B">B</a>]</span>


</div>
Expand Down Expand Up @@ -233,7 +235,7 @@ <h1 class="modulename">
</span></pre></div>


<div class="docstring"><p>This class is defined in .child_c and inherits from .child_b.B</p>
<div class="docstring"><p>This class is defined in <a href="demopackage/child_c.html">demopackage.child_c</a> and inherits from <a href="#B">B</a></p>
</div>


Expand Down
2 changes: 1 addition & 1 deletion test/testdata/demopackage.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<module demopackage # A test package
<module demopackage # A test package with …
<class demopackage.Test # inherited from demopackage._child_d.Test, The Test class from …
<method def __init__(): ... # inherited from demopackage._child_d.Test.__init__>
<method def foo(self, a: int): ... # inherited from demopackage._child_d.Test.foo, Do foo.>
Expand Down
3 changes: 2 additions & 1 deletion test/testdata/demopackage/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""A test package"""
"""A test package with a sub-package at `.subpackage`."""
import demopackage2

from . import _child_e
Expand All @@ -19,4 +19,5 @@
"demopackage2",
"_child_e",
"child_excluded",
"subpackage",
]
2 changes: 1 addition & 1 deletion test/testdata/demopackage/child_b.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class B:
"""This class is defined in .child_b."""
"""This class is defined in .child_b. It has a B.b method."""

b_type: typing.Type[B]
"""we have a self-referential attribute here"""
Expand Down
11 changes: 11 additions & 0 deletions test/testdata/demopackage/subpackage/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
A sub-package.
It imports and re-exposes ..child_b.B, and links to `..C` .
"""

from ..child_b import B

__all__ = [
"B"
]
361 changes: 348 additions & 13 deletions test/testdata/demopackage_dir.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/testdata/example_customtemplate.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ <h1 class="modulename">
</div>
<div id="Dog.friends" class="classattr">
<div class="attr variable">
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">demo.Dog</a>]</span>
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">Dog</a>]</span>


</div>
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/example_darkmode.html
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ <h1 class="modulename">
</div>
<div id="Dog.friends" class="classattr">
<div class="attr variable">
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">demo.Dog</a>]</span>
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">Dog</a>]</span>


</div>
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/example_mkdocs.html
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ <h1 class="modulename">
</div>
<div id="Dog.friends" class="classattr">
<div class="attr variable">
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">demo.Dog</a>]</span>
<span class="name">friends</span><span class="annotation">: list[<a href="#Dog">Dog</a>]</span>


</div>
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/mermaid_demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ <h1 class="modulename">
</div>
<div id="Pet.friends" class="classattr">
<div class="attr variable">
<span class="name">friends</span><span class="annotation">: list[<a href="#Pet">mermaid_demo.Pet</a>]</span>
<span class="name">friends</span><span class="annotation">: list[<a href="#Pet">Pet</a>]</span>


</div>
Expand Down
8 changes: 4 additions & 4 deletions test/testdata/misc.html
Original file line number Diff line number Diff line change
Expand Up @@ -2067,7 +2067,7 @@ <h6 id="heading-6">Heading 6</h6>
<section id="another_decorated_function">
<div class="attr variable">
<span class="name">another_decorated_function</span> =
<span class="default_value">&lt;<a href="#ClassDecorator">misc.ClassDecorator</a> object&gt;</span>
<span class="default_value">&lt;<a href="#ClassDecorator">ClassDecorator</a> object&gt;</span>


</div>
Expand Down Expand Up @@ -2104,7 +2104,7 @@ <h6 id="heading-6">Heading 6</h6>
<input id="SubclassRef.__init__-view-source" class="view-source-toggle-state" type="checkbox" aria-hidden="true" tabindex="-1">
<div class="attr function">

<span class="name">SubclassRef</span><span class="signature pdoc-code condensed">(<span class="param"><span class="n">x</span><span class="p">:</span> <span class="n"><a href="#SubclassRef.SubClass">misc.SubclassRef.SubClass</a></span></span>)</span>
<span class="name">SubclassRef</span><span class="signature pdoc-code condensed">(<span class="param"><span class="n">x</span><span class="p">:</span> <span class="n"><a href="#SubclassRef.SubClass">SubclassRef.SubClass</a></span></span>)</span>

<label class="view-source-button" for="SubclassRef.__init__-view-source"><span>View Source</span></label>

Expand Down Expand Up @@ -2163,7 +2163,7 @@ <h6 id="heading-6">Heading 6</h6>
<div id="ClassAsAttribute.static_attr_to_class" class="classattr">
<div class="attr variable">
<span class="name">static_attr_to_class</span> =
<span class="default_value">&lt;class &#39;<a href="#ClassDecorator">misc.ClassDecorator</a>&#39;&gt;</span>
<span class="default_value">&lt;class &#39;<a href="#ClassDecorator">ClassDecorator</a>&#39;&gt;</span>


</div>
Expand All @@ -2177,7 +2177,7 @@ <h6 id="heading-6">Heading 6</h6>
<div id="ClassAsAttribute.static_attr_to_instance" class="classattr">
<div class="attr variable">
<span class="name">static_attr_to_instance</span> =
<span class="default_value">&lt;<a href="#ClassDecorator">misc.ClassDecorator</a> object&gt;</span>
<span class="default_value">&lt;<a href="#ClassDecorator">ClassDecorator</a> object&gt;</span>


</div>
Expand Down
Loading

0 comments on commit c49da24

Please sign in to comment.