From 9e4f217be3a69681097a5de7045f48cb8017a069 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Thu, 22 Aug 2024 16:44:56 -0400 Subject: [PATCH 01/14] feat!: initial description-list support; render_header cleanup --- docs/_quarto.yml | 3 + quartodoc/renderers/md_renderer.py | 104 +++++++++++++--- .../tests/__snapshots__/test_renderers.ambr | 114 ++++++++++++++++++ quartodoc/tests/test_renderers.py | 36 ++++++ 4 files changed, 238 insertions(+), 19 deletions(-) diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 0ec437c..8d19b3e 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -88,6 +88,9 @@ quartodoc: dir: api package: quartodoc render_interlinks: true + renderer: + style: markdown + table_style: description-list sidebar: "api/_sidebar.yml" sections: - title: Preparation Functions diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index bb4194f..444a463 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -9,8 +9,10 @@ from .._griffe_compat import expressions as expr from tabulate import tabulate from plum import dispatch -from typing import Tuple, Union, Optional +from typing import Any, Tuple, Union, Optional from quartodoc import layout +from quartodoc.pandoc.blocks import DefinitionList +from quartodoc.pandoc.inlines import Span, Attr from .base import Renderer, escape, sanitize, convert_rst_link_to_md @@ -22,6 +24,44 @@ def _has_attr_section(el: dc.Docstring | None): return any([isinstance(x, ds.DocstringSectionAttributes) for x in el.parsed]) +def _name_description(row: list[str | None]): + if len(row) == 4: + name, anno, desc, default = row + elif len(row) == 3: + name, anno, desc = row + default = None + elif len(row) == 2: + anno, desc = row + name, default = None, None + else: + raise ValueError(f"Unsupported row length: {len(row)}") + + part_name = ( + Span(name, Attr(classes=["parameter-name"])) + if name is not None + else '' + ) + part_anno = ( + Span(anno, Attr(classes=["parameter-annotation"])) + if anno is not None + else '' + ) + + part_default = ( + Span(" = ", Attr(classes=["parameter-default-sep"])) + if default is not None + else '' + ) + + part_desc = desc if desc is not None else "" + + anno_sep = Span(":", Attr(classes=["parameter-annotation-sep"])) + + return (f"{part_name}{anno_sep}{part_anno}{part_default}", part_desc) + + + + class MdRenderer(Renderer): """Render docstrings to markdown. @@ -61,6 +101,8 @@ def __init__( display_name: str = "relative", hook_pre=None, render_interlinks=False, + #table_style="description-list", + table_style="table", ): self.header_level = header_level self.show_signature = show_signature @@ -68,6 +110,7 @@ def __init__( self.display_name = display_name self.hook_pre = hook_pre self.render_interlinks = render_interlinks + self.table_style = table_style self.crnt_header_level = self.header_level @@ -103,8 +146,10 @@ def _fetch_method_parameters(self, el: dc.Function): return el.parameters def _render_table(self, rows, headers): - table = tabulate(rows, headers=headers, tablefmt="github") + if self.table_style == "description-list": + return str(DefinitionList(list(map(_name_description, rows)))) + table = tabulate(rows, headers=headers, tablefmt="github") return table # render_annotation method -------------------------------------------------------- @@ -174,7 +219,7 @@ def signature( return f"`{name}`" @dispatch - def render_header(self, el: layout.Doc): + def render_header(self, el: layout.Doc) -> str: """Render the header of a docstring, including any anchors.""" _str_dispname = el.name @@ -182,6 +227,11 @@ def render_header(self, el: layout.Doc): # e.g. get_object, rather than quartodoc.get_object _anchor = f"{{ #{el.obj.path} }}" return f"{'#' * self.crnt_header_level} {_str_dispname} {_anchor}" + + @dispatch + def render_header(self, el: ds.DocstringSection) -> str: + title = el.title or el.kind.value + return f"{'#' * self.crnt_header_level} {title.title()}" # render method ----------------------------------------------------------- @@ -336,7 +386,8 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]): str_sig = self.signature(el) sig_part = [str_sig] if self.show_signature else [] - body = self.render(el.obj) + with self._increment_header(): + body = self.render(el.obj) return "\n\n".join( [title, *sig_part, body, *attr_docs, *class_docs, *meth_docs] @@ -349,7 +400,10 @@ def render(self, el: Union[layout.DocFunction, layout.DocAttribute]): str_sig = self.signature(el) sig_part = [str_sig] if self.show_signature else [] - return "\n\n".join([title, *sig_part, self.render(el.obj)]) + with self._increment_header(): + body = self.render(el.obj) + + return "\n\n".join([title, *sig_part, body]) # render griffe objects =================================================== @@ -357,20 +411,26 @@ def render(self, el: Union[layout.DocFunction, layout.DocAttribute]): def render(self, el: Union[dc.Object, dc.Alias]): """Render high level objects representing functions, classes, etc..""" - str_body = [] if el.docstring is None: - pass + return "" else: - patched_sections = qast.transform(el.docstring.parsed) - for section in patched_sections: - title = section.title or section.kind.value - body = self.render(section) + return self.render(el.docstring) + + @dispatch + def render(self, el: dc.Docstring): + str_body = [] + patched_sections = qast.transform(el.parsed) + + for section in patched_sections: + title = section.title or section.kind.value + body: str = self.render(section) - if title != "text": - header = f"{'#' * (self.crnt_header_level + 1)} {title.title()}" - str_body.append("\n\n".join([header, body])) - else: - str_body.append(body) + if title != "text": + header = self.render_header(section) + # header = f"{'#' * (self.crnt_header_level + 1)} {title.title()}" + str_body.append("\n\n".join([header, body])) + else: + str_body.append(body) parts = [*str_body] @@ -550,15 +610,21 @@ def render(self, el: qast.ExampleText): @dispatch def render(self, el: Union[ds.DocstringSectionReturns, ds.DocstringSectionRaises]): rows = list(map(self.render, el.value)) - header = ["Type", "Description"] + header = ["Name", "Type", "Description"] return self._render_table(rows, header) @dispatch - def render(self, el: Union[ds.DocstringReturn, ds.DocstringRaise]): + def render(self, el: ds.DocstringReturn): + # similar to DocstringParameter, but no name or default + annotation = self.render_annotation(el.annotation) + return (el.name, annotation, sanitize(el.description, allow_markdown=True)) + + @dispatch + def render(self, el: ds.DocstringRaise): # similar to DocstringParameter, but no name or default annotation = self.render_annotation(el.annotation) - return (annotation, sanitize(el.description, allow_markdown=True)) + return ("", annotation, sanitize(el.description, allow_markdown=True)) # unsupported parts ---- diff --git a/quartodoc/tests/__snapshots__/test_renderers.ambr b/quartodoc/tests/__snapshots__/test_renderers.ambr index 719abe0..f47ebb4 100644 --- a/quartodoc/tests/__snapshots__/test_renderers.ambr +++ b/quartodoc/tests/__snapshots__/test_renderers.ambr @@ -364,3 +364,117 @@ | `b` | str | The b parameter. | _required_ | ''' # --- +# name: test_render_numpydoc_section_return[int\n A description.] + ''' + Code + Parameters + --- + int + A description. + + Returns + --- + int + A description. + + Attributes + --- + int + A description. + + Default + # Parameters + + | Name | Type | Description | Default | + |--------|--------|----------------|------------| + | `int` | | A description. | _required_ | + + # Returns + + | Name | Type | Description | + |--------|--------|----------------| + | | int | A description. | + + # Attributes + + | Name | Type | Description | + |--------|--------|----------------| + | int | | A description. | + + List + # Parameters + + [`int`]{.parameter-name}[:]{.parameter-annotation-sep}[]{.parameter-annotation}[ = ]{.parameter-default-sep} + + : A description. + + # Returns + + []{.parameter-name}[:]{.parameter-annotation-sep}[int]{.parameter-annotation} + + : A description. + + # Attributes + + [int]{.parameter-name}[:]{.parameter-annotation-sep}[]{.parameter-annotation} + + : A description. + ''' +# --- +# name: test_render_numpydoc_section_return[name: int\n A description.] + ''' + Code + Parameters + --- + name: int + A description. + + Returns + --- + name: int + A description. + + Attributes + --- + name: int + A description. + + Default + # Parameters + + | Name | Type | Description | Default | + |--------|--------|----------------|------------| + | `name` | | A description. | _required_ | + + # Returns + + | Name | Type | Description | + |--------|--------|----------------| + | name | int | A description. | + + # Attributes + + | Name | Type | Description | + |--------|--------|----------------| + | name | int | A description. | + + List + # Parameters + + [`name`]{.parameter-name}[:]{.parameter-annotation-sep}[]{.parameter-annotation}[ = ]{.parameter-default-sep} + + : A description. + + # Returns + + [name]{.parameter-name}[:]{.parameter-annotation-sep}[int]{.parameter-annotation} + + : A description. + + # Attributes + + [name]{.parameter-name}[:]{.parameter-annotation-sep}[int]{.parameter-annotation} + + : A description. + ''' +# --- diff --git a/quartodoc/tests/test_renderers.py b/quartodoc/tests/test_renderers.py index c789e46..4a5de45 100644 --- a/quartodoc/tests/test_renderers.py +++ b/quartodoc/tests/test_renderers.py @@ -1,10 +1,17 @@ import pytest +from quartodoc._griffe_compat import dataclasses as dc from quartodoc._griffe_compat import docstrings as ds from quartodoc._griffe_compat import expressions as exp from quartodoc.renderers import MdRenderer from quartodoc import layout, get_object, blueprint, Auto +from textwrap import indent + + +def indented_sections(**kwargs: str): + return "\n\n".join([f"{k}\n" + indent(v, " " * 4) for k, v in kwargs.items()]) + @pytest.fixture def renderer(): @@ -194,3 +201,32 @@ def test_render_doc_signature_name_alias_of_alias(snapshot, renderer): res = renderer.render(bp) assert res == snapshot + + +@pytest.mark.parametrize( + "doc", + [ + """name: int\n A description.""", + """int\n A description.""", + ], +) +def test_render_numpydoc_section_return(snapshot, doc): + from quartodoc.parsers import get_parser_defaults + from griffe.docstrings.parsers import Parser + + full_doc = ( + f"""Parameters\n---\n{doc}\n\nReturns\n---\n{doc}\n\nAttributes\n---\n{doc}""" + ) + + el = dc.Docstring( + value=full_doc, parser=Parser.numpy, parser_options=get_parser_defaults("numpy") + ) + + assert el.parsed is not None and len(el.parsed) == 3 + + res_default = MdRenderer().render(el) + res_list = MdRenderer(table_style="description-list").render(el) + + assert snapshot == indented_sections( + Code=full_doc, Default=res_default, List=res_list + ) From d0e47bcea6fe0021154276d842a2c01d4c2523e2 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Mon, 22 Jan 2024 15:20:19 -0500 Subject: [PATCH 02/14] feat: description-list wraps param line in code --- quartodoc/renderers/md_renderer.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index 444a463..cd45843 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -12,7 +12,7 @@ from typing import Any, Tuple, Union, Optional from quartodoc import layout from quartodoc.pandoc.blocks import DefinitionList -from quartodoc.pandoc.inlines import Span, Attr +from quartodoc.pandoc.inlines import Span, Attr, Code, Inlines from .base import Renderer, escape, sanitize, convert_rst_link_to_md @@ -47,9 +47,13 @@ def _name_description(row: list[str | None]): else '' ) + # TODO: _required_ is set when parsing parameters, but only used + # in the table display format, not description lists.... + # by this stage _required_ is basically a special token to indicate + # a required argument. part_default = ( - Span(" = ", Attr(classes=["parameter-default-sep"])) - if default is not None + Span(f" = {default}", Attr(classes=["parameter-default-sep"])) + if default is not None and default != "_required_" else '' ) @@ -57,7 +61,9 @@ def _name_description(row: list[str | None]): anno_sep = Span(":", Attr(classes=["parameter-annotation-sep"])) - return (f"{part_name}{anno_sep}{part_anno}{part_default}", part_desc) + # TODO: should code wrap the whole thing like this? + param = Code(str(Inlines([part_name, anno_sep, part_anno, part_default]))).html + return (param, part_desc) @@ -231,7 +237,9 @@ def render_header(self, el: layout.Doc) -> str: @dispatch def render_header(self, el: ds.DocstringSection) -> str: title = el.title or el.kind.value - return f"{'#' * self.crnt_header_level} {title.title()}" + _classes = [".doc-section", ".doc-section-" + title.replace(" ", "-")] + _str_classes = " ".join(_classes) + return f"{'#' * self.crnt_header_level} {title.title()} {{ {_str_classes} }}" # render method ----------------------------------------------------------- From 420db323e8396eb4f3804011853d5113fef689a7 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Thu, 22 Aug 2024 17:00:11 -0400 Subject: [PATCH 03/14] chore: clean up a bit --- quartodoc/renderers/md_renderer.py | 22 +++++++--------------- quartodoc/tests/test_renderers.py | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index cd45843..f30e480 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -35,16 +35,10 @@ def _name_description(row: list[str | None]): name, default = None, None else: raise ValueError(f"Unsupported row length: {len(row)}") - - part_name = ( - Span(name, Attr(classes=["parameter-name"])) - if name is not None - else '' - ) + + part_name = Span(name, Attr(classes=["parameter-name"])) if name is not None else "" part_anno = ( - Span(anno, Attr(classes=["parameter-annotation"])) - if anno is not None - else '' + Span(anno, Attr(classes=["parameter-annotation"])) if anno is not None else "" ) # TODO: _required_ is set when parsing parameters, but only used @@ -54,7 +48,7 @@ def _name_description(row: list[str | None]): part_default = ( Span(f" = {default}", Attr(classes=["parameter-default-sep"])) if default is not None and default != "_required_" - else '' + else "" ) part_desc = desc if desc is not None else "" @@ -66,8 +60,6 @@ def _name_description(row: list[str | None]): return (param, part_desc) - - class MdRenderer(Renderer): """Render docstrings to markdown. @@ -107,7 +99,7 @@ def __init__( display_name: str = "relative", hook_pre=None, render_interlinks=False, - #table_style="description-list", + # table_style="description-list", table_style="table", ): self.header_level = header_level @@ -233,13 +225,13 @@ def render_header(self, el: layout.Doc) -> str: # e.g. get_object, rather than quartodoc.get_object _anchor = f"{{ #{el.obj.path} }}" return f"{'#' * self.crnt_header_level} {_str_dispname} {_anchor}" - + @dispatch def render_header(self, el: ds.DocstringSection) -> str: title = el.title or el.kind.value _classes = [".doc-section", ".doc-section-" + title.replace(" ", "-")] _str_classes = " ".join(_classes) - return f"{'#' * self.crnt_header_level} {title.title()} {{ {_str_classes} }}" + return f"{'#' * self.crnt_header_level} {title.title()} {{{_str_classes}}}" # render method ----------------------------------------------------------- diff --git a/quartodoc/tests/test_renderers.py b/quartodoc/tests/test_renderers.py index 4a5de45..43fa05b 100644 --- a/quartodoc/tests/test_renderers.py +++ b/quartodoc/tests/test_renderers.py @@ -212,7 +212,7 @@ def test_render_doc_signature_name_alias_of_alias(snapshot, renderer): ) def test_render_numpydoc_section_return(snapshot, doc): from quartodoc.parsers import get_parser_defaults - from griffe.docstrings.parsers import Parser + from griffe import Parser full_doc = ( f"""Parameters\n---\n{doc}\n\nReturns\n---\n{doc}\n\nAttributes\n---\n{doc}""" From 5e36bccbdc28966fd6b387bbde15e0a37b296d5b Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Thu, 22 Aug 2024 17:00:19 -0400 Subject: [PATCH 04/14] tests: update snapshots --- .../tests/__snapshots__/test_renderers.ambr | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/quartodoc/tests/__snapshots__/test_renderers.ambr b/quartodoc/tests/__snapshots__/test_renderers.ambr index f47ebb4..d9564bb 100644 --- a/quartodoc/tests/__snapshots__/test_renderers.ambr +++ b/quartodoc/tests/__snapshots__/test_renderers.ambr @@ -5,7 +5,7 @@ `tests.example_signature.a_complex_signature(x: [list](`list`)\[[C](`quartodoc.tests.example_signature.C`) \| [int](`int`) \| None\], y: [pathlib](`pathlib`).[Pathlib](`pathlib.Pathlib`))` - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------------------------------------------------------------------------------------|-----------------|------------| @@ -19,7 +19,7 @@ `tests.example_signature.a_complex_signature(x: list\[C \| int \| None\], y: pathlib.Pathlib)` - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------------------------|-----------------|------------| @@ -38,7 +38,7 @@ The extended summary, which may be multiple lines. - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|----------------------|------------| @@ -96,7 +96,7 @@ The extended summary, which may be multiple lines. - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|----------------------|------------| @@ -151,7 +151,7 @@ The short summary. - ## Attributes + ## Attributes {.doc-section .doc-section-attributes} | Name | Type | Description | |--------|--------|---------------------| @@ -300,7 +300,7 @@ A numpy style docstring. - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|------------------|------------| @@ -316,14 +316,14 @@ A google style docstring. - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|------------------|------------| | `a` | int | The a parameter. | _required_ | | `b` | str | The b parameter. | _required_ | - ## Custom Admonition + ## Custom Admonition {.doc-section .doc-section-Custom-Admonition} Some text. ''' @@ -336,14 +336,14 @@ A numpy style docstring. - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|------------------|------------| | `a` | | The a parameter. | _required_ | | `b` | str | The b parameter. | _required_ | - ## Custom Admonition + ## Custom Admonition {.doc-section .doc-section-Custom-Admonition} Some text. ''' @@ -356,7 +356,7 @@ A sphinx style docstring. - ## Parameters + ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|------------------|------------| @@ -383,40 +383,40 @@ A description. Default - # Parameters + # Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|----------------|------------| | `int` | | A description. | _required_ | - # Returns + # Returns {.doc-section .doc-section-returns} | Name | Type | Description | |--------|--------|----------------| | | int | A description. | - # Attributes + # Attributes {.doc-section .doc-section-attributes} | Name | Type | Description | |--------|--------|----------------| | int | | A description. | List - # Parameters + # Parameters {.doc-section .doc-section-parameters} - [`int`]{.parameter-name}[:]{.parameter-annotation-sep}[]{.parameter-annotation}[ = ]{.parameter-default-sep} + [`int`]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} : A description. - # Returns + # Returns {.doc-section .doc-section-returns} - []{.parameter-name}[:]{.parameter-annotation-sep}[int]{.parameter-annotation} + []{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} : A description. - # Attributes + # Attributes {.doc-section .doc-section-attributes} - [int]{.parameter-name}[:]{.parameter-annotation-sep}[]{.parameter-annotation} + [int]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} : A description. ''' @@ -440,40 +440,40 @@ A description. Default - # Parameters + # Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------|----------------|------------| | `name` | | A description. | _required_ | - # Returns + # Returns {.doc-section .doc-section-returns} | Name | Type | Description | |--------|--------|----------------| | name | int | A description. | - # Attributes + # Attributes {.doc-section .doc-section-attributes} | Name | Type | Description | |--------|--------|----------------| | name | int | A description. | List - # Parameters + # Parameters {.doc-section .doc-section-parameters} - [`name`]{.parameter-name}[:]{.parameter-annotation-sep}[]{.parameter-annotation}[ = ]{.parameter-default-sep} + [`name`]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} : A description. - # Returns + # Returns {.doc-section .doc-section-returns} - [name]{.parameter-name}[:]{.parameter-annotation-sep}[int]{.parameter-annotation} + [name]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} : A description. - # Attributes + # Attributes {.doc-section .doc-section-attributes} - [name]{.parameter-name}[:]{.parameter-annotation-sep}[int]{.parameter-annotation} + [name]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} : A description. ''' From 4b3e02c7f57c47405867bb25cff873b7f8020e5d Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Fri, 23 Aug 2024 17:20:43 -0400 Subject: [PATCH 05/14] fix: escape avoids conversion to smartquotes --- quartodoc/renderers/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/quartodoc/renderers/base.py b/quartodoc/renderers/base.py index b9f5141..d8a8968 100644 --- a/quartodoc/renderers/base.py +++ b/quartodoc/renderers/base.py @@ -17,6 +17,9 @@ def sanitize(val: str, allow_markdown=False): # sanitize common tokens that break tables res = val.replace("\n", " ").replace("|", "\\|") + # sanitize elements that get turned into smart quotes + res = res.replace("'", r"\'").replace('"', r"\"") + # sanitize elements that can get interpreted as markdown links # or citations if not allow_markdown: From 81bfa07a51ecc478a1bfe7c9679f0acc45d0dd50 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Fri, 23 Aug 2024 17:21:53 -0400 Subject: [PATCH 06/14] refactor: clean up table rendering using ParamRow class --- quartodoc/renderers/md_renderer.py | 141 ++++++++++++++++++----------- 1 file changed, 87 insertions(+), 54 deletions(-) diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index f30e480..0720986 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -3,16 +3,17 @@ import quartodoc.ast as qast from contextlib import contextmanager +from dataclasses import dataclass from functools import wraps from .._griffe_compat import docstrings as ds from .._griffe_compat import dataclasses as dc from .._griffe_compat import expressions as expr from tabulate import tabulate from plum import dispatch -from typing import Any, Tuple, Union, Optional +from typing import Any, Literal, Tuple, Union, Optional from quartodoc import layout from quartodoc.pandoc.blocks import DefinitionList -from quartodoc.pandoc.inlines import Span, Attr, Code, Inlines +from quartodoc.pandoc.inlines import Span, Strong, Attr, Code, Inlines from .base import Renderer, escape, sanitize, convert_rst_link_to_md @@ -24,40 +25,64 @@ def _has_attr_section(el: dc.Docstring | None): return any([isinstance(x, ds.DocstringSectionAttributes) for x in el.parsed]) -def _name_description(row: list[str | None]): - if len(row) == 4: - name, anno, desc, default = row - elif len(row) == 3: - name, anno, desc = row - default = None - elif len(row) == 2: - anno, desc = row - name, default = None, None - else: - raise ValueError(f"Unsupported row length: {len(row)}") - - part_name = Span(name, Attr(classes=["parameter-name"])) if name is not None else "" - part_anno = ( - Span(anno, Attr(classes=["parameter-annotation"])) if anno is not None else "" - ) +@dataclass +class ParamRow: + name: str | None + description: str + annotation: str | None = None + default: str | None = None - # TODO: _required_ is set when parsing parameters, but only used - # in the table display format, not description lists.... - # by this stage _required_ is basically a special token to indicate - # a required argument. - part_default = ( - Span(f" = {default}", Attr(classes=["parameter-default-sep"])) - if default is not None and default != "_required_" - else "" - ) + def to_definition_list(self): + name = self.name + anno = self.annotation + desc = self.description + default = sanitize(str(self.default)) + + part_name = ( + Span(Strong(name), Attr(classes=["parameter-name"])) + if name is not None + else "" + ) + part_anno = ( + Span(anno, Attr(classes=["parameter-annotation"])) + if anno is not None + else "" + ) - part_desc = desc if desc is not None else "" + # TODO: _required_ is set when parsing parameters, but only used + # in the table display format, not description lists.... + # by this stage _required_ is basically a special token to indicate + # a required argument. + if default is not None: + part_default_sep = Span(" = ", Attr(classes=["parameter-default-sep"])) + part_default = Span(default, Attr(classes=["parameter-default"])) + else: + part_default_sep = "" + part_default = "" + + part_desc = desc if desc is not None else "" + + anno_sep = Span(":", Attr(classes=["parameter-annotation-sep"])) + + # TODO: should code wrap the whole thing like this? + param = Code( + str( + Inlines( + [part_name, anno_sep, part_anno, part_default_sep, part_default] + ) + ) + ).html + return (param, part_desc) - anno_sep = Span(":", Attr(classes=["parameter-annotation-sep"])) + def to_tuple(self, style: Literal["paramters", "attributes", "returns"]): + if style == "parameters": + return (self.name, self.annotation, self.description, self.default) + elif style == "attributes": + return (self.name, self.annotation, self.description) + elif style == "returns": + return (self.name, self.annotation, self.description) - # TODO: should code wrap the whole thing like this? - param = Code(str(Inlines([part_name, anno_sep, part_anno, part_default]))).html - return (param, part_desc) + raise NotImplementedError(f"Unsupported table style: {style}") class MdRenderer(Renderer): @@ -143,12 +168,18 @@ def _fetch_method_parameters(self, el: dc.Function): return el.parameters - def _render_table(self, rows, headers): + def _render_table( + self, + rows, + headers, + style: Literal["parameters", "attributes", "returns"] = "parameters", + ): if self.table_style == "description-list": - return str(DefinitionList(list(map(_name_description, rows)))) - - table = tabulate(rows, headers=headers, tablefmt="github") - return table + return str(DefinitionList([row.to_definition_list() for row in rows])) + else: + row_tuples = [row.to_tuple(style) for row in rows] + table = tabulate(row_tuples, headers=headers, tablefmt="github") + return table # render_annotation method -------------------------------------------------------- @@ -523,18 +554,15 @@ def render(self, el: ds.DocstringSectionText): @dispatch def render(self, el: ds.DocstringSectionParameters): - rows = list(map(self.render, el.value)) + rows: "list[ParamRow]" = list(map(self.render, el.value)) header = ["Name", "Type", "Description", "Default"] return self._render_table(rows, header) @dispatch - def render(self, el: ds.DocstringParameter) -> Tuple[str]: - # TODO: if default is not, should return the word "required" (unescaped) - default = "_required_" if el.default is None else escape(el.default) - + def render(self, el: ds.DocstringParameter) -> ParamRow: annotation = self.render_annotation(el.annotation) clean_desc = sanitize(el.description, allow_markdown=True) - return (escape(el.name), annotation, clean_desc, default) + return ParamRow(el.name, clean_desc, annotation=annotation, default=el.default) # attributes ---- @@ -546,13 +574,12 @@ def render(self, el: ds.DocstringSectionAttributes): return self._render_table(rows, header) @dispatch - def render(self, el: ds.DocstringAttribute): - row = [ - sanitize(el.name), - self.render_annotation(el.annotation), + def render(self, el: ds.DocstringAttribute) -> ParamRow: + return ParamRow( + el.name, sanitize(el.description or "", allow_markdown=True), - ] - return row + annotation=self.render_annotation(el.annotation), + ) # admonition ---- # note this can be a see-also, warnings, or notes section @@ -617,14 +644,20 @@ def render(self, el: Union[ds.DocstringSectionReturns, ds.DocstringSectionRaises @dispatch def render(self, el: ds.DocstringReturn): # similar to DocstringParameter, but no name or default - annotation = self.render_annotation(el.annotation) - return (el.name, annotation, sanitize(el.description, allow_markdown=True)) + return ParamRow( + el.name, + sanitize(el.description, allow_markdown=True), + annotation=self.render_annotation(el.annotation), + ) @dispatch - def render(self, el: ds.DocstringRaise): + def render(self, el: ds.DocstringRaise) -> ParamRow: # similar to DocstringParameter, but no name or default - annotation = self.render_annotation(el.annotation) - return ("", annotation, sanitize(el.description, allow_markdown=True)) + return ParamRow( + None, + sanitize(el.description, allow_markdown=True), + annotation=self.render_annotation(el.annotation), + ) # unsupported parts ---- From f3dd5179f43a3f2ff475a15a302b9b344e8be224 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Fri, 23 Aug 2024 17:23:11 -0400 Subject: [PATCH 07/14] feat: use black to format long call signatures --- pyproject.toml | 1 + quartodoc/renderers/md_renderer.py | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 02ebe57..95c2f2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ classifiers = [ dynamic = ["version"] requires-python = ">=3.9" dependencies = [ + "black", "click", "griffe >= 0.33", "sphobjinv >= 2.3.1", diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index 0720986..9d4aa55 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -1,5 +1,6 @@ from __future__ import annotations +import black import quartodoc.ast as qast from contextlib import contextmanager @@ -238,7 +239,13 @@ def signature( name = self._fetch_object_dispname(source or el) pars = self.render(self._fetch_method_parameters(el)) - return f"`{name}({pars})`" + flat_sig = f"{name}({pars})" + if len(flat_sig) > 80: + sig = black.format_str(flat_sig, mode=black.Mode()) + else: + sig = flat_sig + + return f"```python\n{sig}\n```" @dispatch def signature( From a256a3e310dcbff11c48c27961499aafb18e5743 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Fri, 23 Aug 2024 17:24:51 -0400 Subject: [PATCH 08/14] docs: commit some basic description list styles --- docs/_quarto.yml | 4 +++- docs/styles-quartodoc.css | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/styles-quartodoc.css diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 8d19b3e..824874e 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -79,7 +79,9 @@ website: format: html: theme: cosmo - css: styles.css + css: + - styles.css + - styles-quartodoc.css toc: true diff --git a/docs/styles-quartodoc.css b/docs/styles-quartodoc.css new file mode 100644 index 0000000..256efe1 --- /dev/null +++ b/docs/styles-quartodoc.css @@ -0,0 +1,12 @@ +.doc-section dt code { + background: none; +} + +.doc-section dt { + background-color: lightyellow; + display: block; +} + +.doc-section dl dd { + margin-left: 3rem; +} From 09c792be16206712ff42d20d23eee9378ad0beb3 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Mon, 26 Aug 2024 15:56:02 -0400 Subject: [PATCH 09/14] fix: restore formatting of table-style items --- quartodoc/renderers/md_renderer.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index 9d4aa55..6e89b6c 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -75,13 +75,15 @@ def to_definition_list(self): ).html return (param, part_desc) - def to_tuple(self, style: Literal["paramters", "attributes", "returns"]): + def to_tuple(self, style: Literal["parameters", "attributes", "returns"]): + name = self.name if style == "parameters": - return (self.name, self.annotation, self.description, self.default) + default = "_required_" if self.default is None else escape(self.default) + return (name, self.annotation, self.description, default) elif style == "attributes": - return (self.name, self.annotation, self.description) + return (name, self.annotation, self.description) elif style == "returns": - return (self.name, self.annotation, self.description) + return (name, self.annotation, self.description) raise NotImplementedError(f"Unsupported table style: {style}") @@ -173,7 +175,7 @@ def _render_table( self, rows, headers, - style: Literal["parameters", "attributes", "returns"] = "parameters", + style: Literal["parameters", "attributes", "returns"], ): if self.table_style == "description-list": return str(DefinitionList([row.to_definition_list() for row in rows])) @@ -563,7 +565,7 @@ def render(self, el: ds.DocstringSectionText): def render(self, el: ds.DocstringSectionParameters): rows: "list[ParamRow]" = list(map(self.render, el.value)) header = ["Name", "Type", "Description", "Default"] - return self._render_table(rows, header) + return self._render_table(rows, header, "parameters") @dispatch def render(self, el: ds.DocstringParameter) -> ParamRow: @@ -578,7 +580,7 @@ def render(self, el: ds.DocstringSectionAttributes): header = ["Name", "Type", "Description"] rows = list(map(self.render, el.value)) - return self._render_table(rows, header) + return self._render_table(rows, header, "attributes") @dispatch def render(self, el: ds.DocstringAttribute) -> ParamRow: @@ -646,7 +648,7 @@ def render(self, el: Union[ds.DocstringSectionReturns, ds.DocstringSectionRaises rows = list(map(self.render, el.value)) header = ["Name", "Type", "Description"] - return self._render_table(rows, header) + return self._render_table(rows, header, "returns") @dispatch def render(self, el: ds.DocstringReturn): From af25133cbd6e8177e803d5370a5b446d158d9d88 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Mon, 26 Aug 2024 16:28:25 -0400 Subject: [PATCH 10/14] fix!: do not sanitize sig params, return to signature as list --- quartodoc/renderers/md_renderer.py | 17 +++++++++-------- quartodoc/tests/test_renderers.py | 12 +++++++----- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index 6e89b6c..ff6bb97 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -241,9 +241,10 @@ def signature( name = self._fetch_object_dispname(source or el) pars = self.render(self._fetch_method_parameters(el)) - flat_sig = f"{name}({pars})" + flat_sig = f"{name}({', '.join(pars)})" if len(flat_sig) > 80: - sig = black.format_str(flat_sig, mode=black.Mode()) + indented = [" " * 4 + par for par in pars] + sig = "\n".join([f"{name}(", *indented, ")"]) else: sig = flat_sig @@ -479,7 +480,7 @@ def render(self, el: dc.Docstring): # signature parts ------------------------------------------------------------- @dispatch - def render(self, el: dc.Parameters): + def render(self, el: dc.Parameters) -> "list[str]": # index for switch from positional to kw args (via an unnamed *) try: kw_only = [par.kind for par in el].index(dc.ParameterKind.keyword_only) @@ -507,15 +508,15 @@ def render(self, el: dc.Parameters): and kw_only > 0 and el[kw_only - 1].kind != dc.ParameterKind.var_positional ): - pars.insert(kw_only, sanitize("*")) + pars.insert(kw_only, "*") # insert a single `/, ` argument to represent shift from positional only arguments # note that this must come before a single *, so it's okay that both this # and block above insert into pars if pos_only is not None: - pars.insert(pos_only + 1, sanitize("/")) + pars.insert(pos_only + 1, "/") - return ", ".join(pars) + return pars @dispatch def render(self, el: dc.Parameter): @@ -530,8 +531,8 @@ def render(self, el: dc.Parameter): else: glob = "" - annotation = self.render_annotation(el.annotation) - name = sanitize(el.name) + annotation = el.annotation # self.render_annotation(el.annotation) + name = el.name if self.show_signature_annotations: if annotation and has_default: diff --git a/quartodoc/tests/test_renderers.py b/quartodoc/tests/test_renderers.py index 43fa05b..d02b0e2 100644 --- a/quartodoc/tests/test_renderers.py +++ b/quartodoc/tests/test_renderers.py @@ -22,7 +22,7 @@ def test_render_param_kwargs(renderer): f = get_object("quartodoc.tests.example_signature.no_annotations") res = renderer.render(f.parameters) - assert res == "a, b=1, *args, c, d=2, **kwargs" + assert ", ".join(res) == "a, b=1, *args, c, d=2, **kwargs" def test_render_param_kwargs_annotated(): @@ -32,8 +32,8 @@ def test_render_param_kwargs_annotated(): res = renderer.render(f.parameters) assert ( - res - == "a: int, b: int = 1, *args: list\[str\], c: int, d: int, **kwargs: dict\[str, str\]" + ", ".join(res) + == "a: int, b: int = 1, *args: list[str], c: int, d: int, **kwargs: dict[str, str]" ) @@ -50,7 +50,7 @@ def test_render_param_kwonly(src, dst, renderer): f = get_object("quartodoc.tests", src) res = renderer.render(f.parameters) - assert res == dst + assert ", ".join(res) == dst @pytest.mark.parametrize( @@ -108,7 +108,9 @@ def test_render_doc_attribute(renderer): res = renderer.render(attr) print(res) - assert res == ["abc", r"Optional\[\]", "xyz"] + assert res.name == "abc" + assert res.annotation == "Optional\[\]" + assert res.description == "xyz" def test_render_doc_section_admonition(renderer): From 419913a4f3b2e4674bedba39c4fb701c43be3188 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Mon, 26 Aug 2024 16:28:39 -0400 Subject: [PATCH 11/14] tests: update snapshots --- .../tests/__snapshots__/test_renderers.ambr | 146 ++++++++++++------ 1 file changed, 99 insertions(+), 47 deletions(-) diff --git a/quartodoc/tests/__snapshots__/test_renderers.ambr b/quartodoc/tests/__snapshots__/test_renderers.ambr index d9564bb..a46143e 100644 --- a/quartodoc/tests/__snapshots__/test_renderers.ambr +++ b/quartodoc/tests/__snapshots__/test_renderers.ambr @@ -3,35 +3,47 @@ ''' # quartodoc.tests.example_signature.a_complex_signature { #quartodoc.tests.example_signature.a_complex_signature } - `tests.example_signature.a_complex_signature(x: [list](`list`)\[[C](`quartodoc.tests.example_signature.C`) \| [int](`int`) \| None\], y: [pathlib](`pathlib`).[Pathlib](`pathlib.Pathlib`))` + ```python + tests.example_signature.a_complex_signature( + x: list[C | int | None] + y: pathlib.Pathlib + ) + ``` ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------------------------------------------------------------------------------------|-----------------|------------| - | `x` | [list](`list`)\[[C](`quartodoc.tests.example_signature.C`) \| [int](`int`) \| None\] | The x parameter | _required_ | - | `y` | [pathlib](`pathlib`).[Pathlib](`pathlib.Pathlib`) | The y parameter | _required_ | + | x | [list](`list`)\[[C](`quartodoc.tests.example_signature.C`) \| [int](`int`) \| None\] | The x parameter | _required_ | + | y | [pathlib](`pathlib`).[Pathlib](`pathlib.Pathlib`) | The y parameter | _required_ | ''' # --- # name: test_render_annotations_complex_no_interlinks ''' # quartodoc.tests.example_signature.a_complex_signature { #quartodoc.tests.example_signature.a_complex_signature } - `tests.example_signature.a_complex_signature(x: list\[C \| int \| None\], y: pathlib.Pathlib)` + ```python + tests.example_signature.a_complex_signature( + x: list[C | int | None] + y: pathlib.Pathlib + ) + ``` ## Parameters {.doc-section .doc-section-parameters} | Name | Type | Description | Default | |--------|--------------------------|-----------------|------------| - | `x` | list\[C \| int \| None\] | The x parameter | _required_ | - | `y` | pathlib.Pathlib | The y parameter | _required_ | + | x | list\[C \| int \| None\] | The x parameter | _required_ | + | y | pathlib.Pathlib | The y parameter | _required_ | ''' # --- # name: test_render_doc_class[embedded] ''' # quartodoc.tests.example_class.C { #quartodoc.tests.example_class.C } - `tests.example_class.C(self, x, y)` + ```python + tests.example_class.C(self, x, y) + ``` The short summary. @@ -42,8 +54,8 @@ | Name | Type | Description | Default | |--------|--------|----------------------|------------| - | `x` | str | Uses signature type. | _required_ | - | `y` | int | Uses manual type. | _required_ | + | x | str | Uses signature type. | _required_ | + | y | int | Uses manual type. | _required_ | ## Attributes @@ -61,7 +73,9 @@ ### D { #quartodoc.tests.example_class.C.D } - `tests.example_class.C.D()` + ```python + tests.example_class.C.D() + ``` A nested class @@ -74,13 +88,17 @@ ### some_class_method { #quartodoc.tests.example_class.C.some_class_method } - `tests.example_class.C.some_class_method()` + ```python + tests.example_class.C.some_class_method() + ``` A class method ### some_method { #quartodoc.tests.example_class.C.some_method } - `tests.example_class.C.some_method()` + ```python + tests.example_class.C.some_method() + ``` A method ''' @@ -89,7 +107,9 @@ ''' # quartodoc.tests.example_class.C { #quartodoc.tests.example_class.C } - `tests.example_class.C(self, x, y)` + ```python + tests.example_class.C(self, x, y) + ``` The short summary. @@ -100,8 +120,8 @@ | Name | Type | Description | Default | |--------|--------|----------------------|------------| - | `x` | str | Uses signature type. | _required_ | - | `y` | int | Uses manual type. | _required_ | + | x | str | Uses signature type. | _required_ | + | y | int | Uses manual type. | _required_ | ## Attributes @@ -119,7 +139,9 @@ ## D { #quartodoc.tests.example_class.C.D } - `tests.example_class.C.D()` + ```python + tests.example_class.C.D() + ``` A nested class @@ -132,13 +154,17 @@ ## some_class_method { #quartodoc.tests.example_class.C.some_class_method } - `tests.example_class.C.some_class_method()` + ```python + tests.example_class.C.some_class_method() + ``` A class method ## some_method { #quartodoc.tests.example_class.C.some_method } - `tests.example_class.C.some_method()` + ```python + tests.example_class.C.some_method() + ``` A method ''' @@ -147,7 +173,9 @@ ''' # quartodoc.tests.example_class.AttributesTable { #quartodoc.tests.example_class.AttributesTable } - `tests.example_class.AttributesTable(self)` + ```python + tests.example_class.AttributesTable(self) + ``` The short summary. @@ -182,7 +210,9 @@ ### AClass { #quartodoc.tests.example.AClass } - `tests.example.AClass()` + ```python + tests.example.AClass() + ``` A class @@ -200,7 +230,9 @@ ##### a_method { #quartodoc.tests.example.AClass.a_method } - `tests.example.AClass.a_method()` + ```python + tests.example.AClass.a_method() + ``` A method @@ -212,7 +244,9 @@ ### a_func { #quartodoc.tests.example.a_func } - `tests.example.a_func()` + ```python + tests.example.a_func() + ``` A function ''' @@ -239,7 +273,9 @@ ## AClass { #quartodoc.tests.example.AClass } - `tests.example.AClass()` + ```python + tests.example.AClass() + ``` A class @@ -257,7 +293,9 @@ #### a_method { #quartodoc.tests.example.AClass.a_method } - `tests.example.AClass.a_method()` + ```python + tests.example.AClass.a_method() + ``` A method @@ -269,7 +307,9 @@ ## a_func { #quartodoc.tests.example.a_func } - `tests.example.a_func()` + ```python + tests.example.a_func() + ``` A function ''' @@ -278,7 +318,9 @@ ''' # example.a_func { #quartodoc.tests.example.a_func } - `a_func()` + ```python + a_func() + ``` A function ''' @@ -287,7 +329,9 @@ ''' # example.a_nested_alias { #quartodoc.tests.example.a_nested_alias } - `tests.example.a_nested_alias()` + ```python + tests.example.a_nested_alias() + ``` A nested alias target ''' @@ -296,7 +340,9 @@ ''' # f_numpy_with_linebreaks { #quartodoc.tests.example_docstring_styles.f_numpy_with_linebreaks } - `tests.example_docstring_styles.f_numpy_with_linebreaks(a, b)` + ```python + tests.example_docstring_styles.f_numpy_with_linebreaks(a, b) + ``` A numpy style docstring. @@ -304,15 +350,17 @@ | Name | Type | Description | Default | |--------|--------|------------------|------------| - | `a` | | The a parameter. | _required_ | - | `b` | str | The b parameter. | _required_ | + | a | | The a parameter. | _required_ | + | b | str | The b parameter. | _required_ | ''' # --- # name: test_render_docstring_styles[google] ''' # f_google { #quartodoc.tests.example_docstring_styles.f_google } - `tests.example_docstring_styles.f_google(a, b)` + ```python + tests.example_docstring_styles.f_google(a, b) + ``` A google style docstring. @@ -320,8 +368,8 @@ | Name | Type | Description | Default | |--------|--------|------------------|------------| - | `a` | int | The a parameter. | _required_ | - | `b` | str | The b parameter. | _required_ | + | a | int | The a parameter. | _required_ | + | b | str | The b parameter. | _required_ | ## Custom Admonition {.doc-section .doc-section-Custom-Admonition} @@ -332,7 +380,9 @@ ''' # f_numpy { #quartodoc.tests.example_docstring_styles.f_numpy } - `tests.example_docstring_styles.f_numpy(a, b)` + ```python + tests.example_docstring_styles.f_numpy(a, b) + ``` A numpy style docstring. @@ -340,8 +390,8 @@ | Name | Type | Description | Default | |--------|--------|------------------|------------| - | `a` | | The a parameter. | _required_ | - | `b` | str | The b parameter. | _required_ | + | a | | The a parameter. | _required_ | + | b | str | The b parameter. | _required_ | ## Custom Admonition {.doc-section .doc-section-Custom-Admonition} @@ -352,7 +402,9 @@ ''' # f_sphinx { #quartodoc.tests.example_docstring_styles.f_sphinx } - `tests.example_docstring_styles.f_sphinx(a, b)` + ```python + tests.example_docstring_styles.f_sphinx(a, b) + ``` A sphinx style docstring. @@ -360,8 +412,8 @@ | Name | Type | Description | Default | |--------|--------|------------------|------------| - | `a` | int | The a parameter. | _required_ | - | `b` | str | The b parameter. | _required_ | + | a | int | The a parameter. | _required_ | + | b | str | The b parameter. | _required_ | ''' # --- # name: test_render_numpydoc_section_return[int\n A description.] @@ -387,7 +439,7 @@ | Name | Type | Description | Default | |--------|--------|----------------|------------| - | `int` | | A description. | _required_ | + | int | | A description. | _required_ | # Returns {.doc-section .doc-section-returns} @@ -404,19 +456,19 @@ List # Parameters {.doc-section .doc-section-parameters} - [`int`]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default} : A description. # Returns {.doc-section .doc-section-returns} - []{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + []{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default} : A description. # Attributes {.doc-section .doc-section-attributes} - [int]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default} : A description. ''' @@ -444,7 +496,7 @@ | Name | Type | Description | Default | |--------|--------|----------------|------------| - | `name` | | A description. | _required_ | + | name | | A description. | _required_ | # Returns {.doc-section .doc-section-returns} @@ -461,19 +513,19 @@ List # Parameters {.doc-section .doc-section-parameters} - [`name`]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default} : A description. # Returns {.doc-section .doc-section-returns} - [name]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default} : A description. # Attributes {.doc-section .doc-section-attributes} - [name]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default} : A description. ''' From 191b4cb279a986ddda9fdd8c4bd135344bbd2914 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Mon, 26 Aug 2024 17:06:45 -0400 Subject: [PATCH 12/14] fix: less type information for backwards compat --- quartodoc/renderers/md_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index ff6bb97..583071e 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -480,7 +480,7 @@ def render(self, el: dc.Docstring): # signature parts ------------------------------------------------------------- @dispatch - def render(self, el: dc.Parameters) -> "list[str]": + def render(self, el: dc.Parameters) -> "list": # index for switch from positional to kw args (via an unnamed *) try: kw_only = [par.kind for par in el].index(dc.ParameterKind.keyword_only) From 36d78c1ad571c1593dc33d0c95e3ab6a68d63cf0 Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Tue, 27 Aug 2024 11:13:23 -0400 Subject: [PATCH 13/14] feat!: generate css file with default styles for description lists --- docs/_quarto.yml | 3 ++- quartodoc/autosummary.py | 26 +++++++++++++++++++ .../static/styles.css | 5 +++- 3 files changed, 32 insertions(+), 2 deletions(-) rename docs/styles-quartodoc.css => quartodoc/static/styles.css (60%) diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 824874e..88cb53b 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -80,8 +80,8 @@ format: html: theme: cosmo css: + - api/_styles-quartodoc.css - styles.css - - styles-quartodoc.css toc: true @@ -94,6 +94,7 @@ quartodoc: style: markdown table_style: description-list sidebar: "api/_sidebar.yml" + css: "api/_styles-quartodoc.css" sections: - title: Preparation Functions desc: | diff --git a/quartodoc/autosummary.py b/quartodoc/autosummary.py index a2242b7..c6bea4e 100644 --- a/quartodoc/autosummary.py +++ b/quartodoc/autosummary.py @@ -427,6 +427,8 @@ class Builder: The output path of the index file, used to list all API functions. sidebar: The output path for a sidebar yaml config (by default no config generated). + css: + The output path for the default css styles. rewrite_all_pages: Whether to rewrite all rendered doc pages, or only those with changes. source_dir: @@ -486,6 +488,7 @@ def __init__( renderer: "dict | Renderer | str" = "markdown", out_index: str = None, sidebar: "str | None" = None, + css: "str | None" = None, rewrite_all_pages=False, source_dir: "str | None" = None, dynamic: bool | None = None, @@ -502,6 +505,7 @@ def __init__( self.dir = dir self.title = title self.sidebar = sidebar + self.css = css self.parser = parser self.renderer = Renderer.from_config(renderer) @@ -587,6 +591,12 @@ def build(self, filter: str = "*"): _log.info(f"Writing sidebar yaml to {self.sidebar}") self.write_sidebar(blueprint) + # css ---- + + if self.css: + _log.info(f"Writing css styles to {self.css}") + self.write_css() + def write_index(self, blueprint: layout.Layout): """Write API index page.""" @@ -685,6 +695,22 @@ def write_sidebar(self, blueprint: layout.Layout): d_sidebar = self._generate_sidebar(blueprint) yaml.dump(d_sidebar, open(self.sidebar, "w")) + def write_css(self): + """Write default css styles to a file.""" + from importlib_resources import files + from importlib_metadata import version + + v = version("quartodoc") + + note = ( + f"/*\nThis file generated automatically by quartodoc version {v}.\n" + "Modifications may be overwritten by quartodoc build. If you want to\n" + "customize styles, create a new .css file to avoid losing changes.\n" + "*/\n\n\n" + ) + with open(files("quartodoc.static") / "styles.css") as f: + Path(self.css).write_text(note + f.read()) + def _page_to_links(self, el: layout.Page) -> list[str]: # if el.flatten: # links = [] diff --git a/docs/styles-quartodoc.css b/quartodoc/static/styles.css similarity index 60% rename from docs/styles-quartodoc.css rename to quartodoc/static/styles.css index 256efe1..a029aba 100644 --- a/docs/styles-quartodoc.css +++ b/quartodoc/static/styles.css @@ -1,9 +1,12 @@ +/* styles for parameter tables, etc.. ---- +*/ + .doc-section dt code { background: none; } .doc-section dt { - background-color: lightyellow; + /* background-color: lightyellow; */ display: block; } From 9005913d8ef9c6c338476eff46acf68cf8d5c95c Mon Sep 17 00:00:00 2001 From: Michael Chow Date: Tue, 27 Aug 2024 12:15:30 -0400 Subject: [PATCH 14/14] docs: update starter _quarto.yml with generated css --- docs/get-started/overview.qmd | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/get-started/overview.qmd b/docs/get-started/overview.qmd index cf67318..4265480 100644 --- a/docs/get-started/overview.qmd +++ b/docs/get-started/overview.qmd @@ -77,15 +77,20 @@ project: # tell quarto to read the generated sidebar metadata-files: - - _sidebar.yml + - api/_sidebar.yml +# tell quarto to read the generated styles +format: + css: + - api/_styles-quartodoc.css quartodoc: # the name used to import the package you want to create reference docs for package: quartodoc - # write sidebar data to this file - sidebar: _sidebar.yml + # write sidebar and style data + sidebar: api/_sidebar.yml + css: api/_styles-quartodoc.css sections: - title: Some functions