Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for showing all overload signatures #665

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 57 additions & 12 deletions pdoc/doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
from typing import TypeVar
from typing import Union
from typing import get_origin

try:
# This only exists on Python 3.11 and later. On older versions,
# we just replace it with a function that does nothing.
from typing import get_overloads
except ImportError: # pragma: no cover
from typing import Sequence

def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]:
return []


import warnings

from pdoc import doc_ast
Expand Down Expand Up @@ -979,16 +991,59 @@ def signature(self) -> inspect.Signature:

If the signature cannot be determined, a placeholder Signature object is returned.
"""
if self.obj is object.__init__:
return self._prepare_signature(self.obj)

@cached_property
def signature_without_self(self) -> inspect.Signature:
"""Like `signature`, but without the first argument.

This is useful to display constructors.
"""
return self.signature.replace(
parameters=list(self.signature.parameters.values())[1:]
)

@cached_property
def overloads(self) -> list[inspect.Signature]:
"""
The function's overloaded signatures, if any.

This should do the same processing as `signature`, but can return a list
of additional signatures when available.
"""
try:
values = get_overloads(self.obj)
return [self._prepare_signature(value) for value in values]
except Exception:
return []

@cached_property
def overloads_without_self(self) -> list[inspect.Signature]:
"""Like `overloads`, but without the first argument.

This is useful to display constructors.
"""
return [
sig.replace(parameters=list(sig.parameters.values())[1:])
for sig in self.overloads
]

def _prepare_signature(self, value: Callable[..., object]) -> inspect.Signature:
"""
A helper method for `signature` and `overloads` which performs
necessary post-processing on a signature object.
"""
if value is object.__init__:
# there is a weird edge case were inspect.signature returns a confusing (self, /, *args, **kwargs)
# signature for the default __init__ method.
return inspect.Signature()
try:
sig = _PrettySignature.from_callable(self.obj)
sig = _PrettySignature.from_callable(value)
except Exception:
return inspect.Signature(
[inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
)

mod = inspect.getmodule(self.obj)
globalns = _safe_getattr(mod, "__dict__", {})
localns = globalns
Expand All @@ -1012,16 +1067,6 @@ def signature(self) -> inspect.Signature:
)
return sig

@cached_property
def signature_without_self(self) -> inspect.Signature:
"""Like `signature`, but without the first argument.

This is useful to display constructors.
"""
return self.signature.replace(
parameters=list(self.signature.parameters.values())[1:]
)


class Variable(Doc[None]):
"""
Expand Down
1 change: 1 addition & 0 deletions pdoc/doc_pyi.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def _patch_doc(target_doc: doc.Doc, stub_mod: doc.Module) -> None:
stub_doc.docstring = ""

target_doc.signature = stub_doc.signature
target_doc.overloads = stub_doc.overloads
target_doc.funcdef = stub_doc.funcdef
target_doc.docstring = stub_doc.docstring or target_doc.docstring
elif isinstance(target_doc, doc.Variable) and isinstance(stub_doc, doc.Variable):
Expand Down
13 changes: 13 additions & 0 deletions pdoc/templates/content.css
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,19 @@ This makes sure that the pdoc styling doesn't leak to the rest of the page when
overflow-x: auto;
}

.pdoc .overloads {
margin-left: 2rem;
}

/* The same as .pdoc .attr, but without the focused/target/hover rules. */
.pdoc .extra-attr {
display: block;
margin: .5rem 0 .5rem;
padding: .4rem .4rem .4rem 1rem;
background-color: var(--accent);
overflow-x: auto;
}

.pdoc .classattr {
margin-left: 2rem;
}
Expand Down
27 changes: 25 additions & 2 deletions pdoc/templates/default/module.html.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,30 @@ See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an
{{- fn.signature_without_self | format_signature(colon=False) | linkify }}
{% else %}
<span class="def">{{ fn.funcdef }}</span>
<span class="name">{{ fn.name }}</span>
{{- fn.signature | format_signature(colon=True) | linkify }}
<span class="name">{{ fn.name }}</span>
{{- fn.signature | format_signature(colon=True) | linkify }}
{% endif %}
{% enddefaultmacro %}
{% defaultmacro overloads(fn) -%}
<div class="overloads">
{% if fn.name == "__init__" %}
{% for sig in fn.overloads_without_self %}
<div class="extra-attr {{ fn.kind }}">
<span class="name">{{ ".".join(fn.qualname.split(".")[:-1]) }}</span>
{{- sig | format_signature(colon=False) | linkify }}
</div>
{% endfor %}
{% else %}
{% for sig in fn.overloads %}
<div class="extra-attr {{ fn.kind }}">
<span class="def">{{ fn.funcdef }}</span>
<span class="name">{{ fn.name }}</span>
{{- sig | format_signature(colon=True) | linkify }}
</div>
{% endfor %}
{% endif %}
</div>
{% enddefaultmacro %}
{% defaultmacro variable(var) -%}
{%- if var.is_type_alias_type %}<span class="def">type</span> {% endif -%}
<span class="name">{{ var.name }}</span>{{ annotation(var) }}{{ default_value(var) }}
Expand Down Expand Up @@ -203,6 +223,9 @@ See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an
{% endif %}
{{ view_source_button(doc) }}
</div>
{% if doc.kind == "function" and doc.overloads|length > 0 %}
{{ overloads(doc) }}
{% endif %}
<a class="headerlink" href="#{{ doc.qualname or doc.name }}"></a>
{{ view_source_code(doc) }}
{{ docstring(doc) }}
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/ast_parsing.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/demo.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions test/testdata/demo_eager.html

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions test/testdata/demo_long.html

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions test/testdata/demopackage.html

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions test/testdata/demopackage_dir.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/example_customtemplate.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/example_darkmode.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/example_mkdocs.html

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions test/testdata/flavors_google.html

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions test/testdata/flavors_numpy.html

Large diffs are not rendered by default.

32 changes: 16 additions & 16 deletions test/testdata/flavors_rst.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions test/testdata/math_demo.html

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions test/testdata/math_misc.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/mermaid_demo.html

Large diffs are not rendered by default.

68 changes: 34 additions & 34 deletions test/testdata/misc.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/misc_py310.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/testdata/misc_py311.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/testdata/misc_py312.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions test/testdata/misc_py39.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion test/testdata/pyo3_sample_library.html

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions test/testdata/render_options.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/top_level_reimports.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/testdata/type_checking_imports.html

Large diffs are not rendered by default.

117 changes: 73 additions & 44 deletions test/testdata/type_stub.html

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions test/testdata/type_stub.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class Class:
attr = 42
"""An attribute"""

def __init__(self, x):
"""An overloaded `__init__` method."""

def meth(self, y):
"""A simple method."""

Expand Down
4 changes: 4 additions & 0 deletions test/testdata/type_stub.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ var: list[str]
class Class:
attr: int

@overload
def __init__(self, x: int): ...
@overload
def __init__(self, x: str): ...
def meth(self, y: bool) -> bool: ...

class Subclass:
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/type_stub.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<function def func(x: str, y: Any, z: Iterable[str]) -> int: ... # A simple function.>
<var var: list[str] = [] # Docstring override f…>
<class type_stub.Class
<method def __init__(): ...>
<method def __init__(*args, **kwds): ... # An overloaded `__ini…>
<var attr: int = 42 # An attribute>
<method def meth(self, y: bool) -> bool: ... # A simple method.>
<class type_stub.Class.Subclass
Expand Down
6 changes: 3 additions & 3 deletions test/testdata/visibility.html

Large diffs are not rendered by default.

Loading