diff --git a/pdoc/doc.py b/pdoc/doc.py index a415c841..b6f7342b 100644 --- a/pdoc/doc.py +++ b/pdoc/doc.py @@ -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 @@ -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 @@ -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]): """ diff --git a/pdoc/doc_pyi.py b/pdoc/doc_pyi.py index f6d465a5..2c5a0cc0 100644 --- a/pdoc/doc_pyi.py +++ b/pdoc/doc_pyi.py @@ -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): diff --git a/pdoc/templates/content.css b/pdoc/templates/content.css index 39b7417c..855de295 100644 --- a/pdoc/templates/content.css +++ b/pdoc/templates/content.css @@ -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; } diff --git a/pdoc/templates/default/module.html.jinja2 b/pdoc/templates/default/module.html.jinja2 index 4c4ed438..8190cfdc 100644 --- a/pdoc/templates/default/module.html.jinja2 +++ b/pdoc/templates/default/module.html.jinja2 @@ -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 %} {{ fn.funcdef }} - {{ fn.name }} - {{- fn.signature | format_signature(colon=True) | linkify }} + {{ fn.name }} + {{- fn.signature | format_signature(colon=True) | linkify }} {% endif %} {% enddefaultmacro %} +{% defaultmacro overloads(fn) -%} +
+ {% if fn.name == "__init__" %} + {% for sig in fn.overloads_without_self %} +
+ {{ ".".join(fn.qualname.split(".")[:-1]) }} + {{- sig | format_signature(colon=False) | linkify }} +
+ {% endfor %} + {% else %} + {% for sig in fn.overloads %} +
+ {{ fn.funcdef }} + {{ fn.name }} + {{- sig | format_signature(colon=True) | linkify }} +
+ {% endfor %} + {% endif %} +
+{% enddefaultmacro %} {% defaultmacro variable(var) -%} {%- if var.is_type_alias_type %}type {% endif -%} {{ var.name }}{{ annotation(var) }}{{ default_value(var) }} @@ -203,6 +223,9 @@ See https://pdoc.dev/docs/pdoc/render_helpers.html#DefaultMacroExtension for an {% endif %} {{ view_source_button(doc) }} + {% if doc.kind == "function" and doc.overloads|length > 0 %} + {{ overloads(doc) }} + {% endif %} {{ view_source_code(doc) }} {{ docstring(doc) }} diff --git a/test/testdata/ast_parsing.html b/test/testdata/ast_parsing.html index f9dcc1c7..645e691d 100644 --- a/test/testdata/ast_parsing.html +++ b/test/testdata/ast_parsing.html @@ -10,7 +10,7 @@ - +