From 70ace7792e514437b05c8e978b53d4c77949898d Mon Sep 17 00:00:00 2001 From: Samuel Pastva Date: Tue, 23 Jan 2024 21:11:05 +0100 Subject: [PATCH 1/6] Add support for showing all `overload` signatures for a function. --- pdoc/doc.py | 85 ++++++++++++++++++++--- pdoc/doc_pyi.py | 1 + pdoc/templates/content.css | 13 ++++ pdoc/templates/default/module.html.jinja2 | 27 ++++++- 4 files changed, 114 insertions(+), 12 deletions(-) diff --git a/pdoc/doc.py b/pdoc/doc.py index a415c841..22b6e914 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: + from typing import Sequence + + def get_overloads(func: Callable[..., object]) -> Sequence[Callable[..., object]]: + return [] + + import warnings from pdoc import doc_ast @@ -989,6 +1001,69 @@ def signature(self) -> inspect.Signature: return inspect.Signature( [inspect.Parameter("unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD)] ) + + return self._process_signature(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:] + ) + + @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. + """ + + if self.obj is object.__init__: + # See `signature`. + return [inspect.Signature()] + + try: + values = get_overloads(self.obj) + except Exception: + return [] + + results = [] + for value in values: + try: + sig = _PrettySignature.from_callable(value) + results.append(self._process_signature(sig)) + except Exception: + sig_err = inspect.Signature( + [ + inspect.Parameter( + "unknown", inspect.Parameter.POSITIONAL_OR_KEYWORD + ) + ] + ) + results.append(sig_err) + return results + + @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(self.signature.parameters.values())[1:]) + for sig in self.overloads + ] + + def _process_signature(self, sig: inspect.Signature) -> inspect.Signature: + """ + A helper method for `signature` and `overloads` which performs + necessary post-processing on a signature object. + """ mod = inspect.getmodule(self.obj) globalns = _safe_getattr(mod, "__dict__", {}) localns = globalns @@ -1012,16 +1087,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) }} From 256d224a0d600c5ca668e48a2ec42cdc9759a21a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:49:37 +0000 Subject: [PATCH 2/6] [autofix.ci] apply automated fixes --- test/testdata/ast_parsing.html | 2 +- test/testdata/demo.html | 4 +- test/testdata/demo_eager.html | 6 +- test/testdata/demo_long.html | 26 ++++----- test/testdata/demopackage.html | 8 +-- test/testdata/demopackage_dir.html | 26 ++++----- test/testdata/example_customtemplate.html | 4 +- test/testdata/example_darkmode.html | 4 +- test/testdata/example_mkdocs.html | 4 +- test/testdata/flavors_google.html | 28 +++++----- test/testdata/flavors_numpy.html | 18 +++--- test/testdata/flavors_rst.html | 32 +++++------ test/testdata/math_demo.html | 6 +- test/testdata/math_misc.html | 10 ++-- test/testdata/mermaid_demo.html | 4 +- test/testdata/misc.html | 68 +++++++++++------------ test/testdata/misc_py310.html | 4 +- test/testdata/misc_py311.html | 2 +- test/testdata/misc_py312.html | 2 +- test/testdata/misc_py39.html | 6 +- test/testdata/pyo3_sample_library.html | 2 +- test/testdata/render_options.html | 10 ++-- test/testdata/top_level_reimports.html | 4 +- test/testdata/type_checking_imports.html | 4 +- test/testdata/type_stub.html | 12 ++-- test/testdata/visibility.html | 6 +- 26 files changed, 151 insertions(+), 151 deletions(-) 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 @@ - +