diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index b36f1bd..75984ae 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -1,18 +1,16 @@ from __future__ import annotations -import black import re 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, Literal, Tuple, Union, Optional +from typing import Literal, Union, Optional from quartodoc import layout from quartodoc.pandoc.blocks import DefinitionList from quartodoc.pandoc.inlines import Span, Strong, Attr, Code, Inlines @@ -190,7 +188,9 @@ def _fetch_object_dispname(self, el: "dc.Alias | dc.Object"): def _fetch_method_parameters(self, el: dc.Function): # adapted from mkdocstrings-python jinja tempalate - if (el.is_class or (el.parent and el.parent.is_class)) and len(el.parameters) > 0: + if (el.is_class or (el.parent and el.parent.is_class)) and len( + el.parameters + ) > 0: if el.parameters[0].name in {"self", "cls"}: return dc.Parameters(*list(el.parameters)[1:]) @@ -407,12 +407,10 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]): # TODO: for now, we skip making an attribute table on classes, unless # they contain an attributes section in the docstring if ( - raw_attrs - and not _has_attr_section(el.obj.docstring) + raw_attrs and not _has_attr_section(el.obj.docstring) # TODO: what should backwards compat be? # and not isinstance(el, layout.DocClass) ): - _attrs_table = "\n".join(map(self.summarize, raw_attrs)) attrs = f"{sub_header} Attributes\n\n{header}\n{_attrs_table}" attr_docs.append(attrs) @@ -699,13 +697,30 @@ def render(self, el: ds.DocstringRaise) -> ParamRow: annotation=self.render_annotation(el.annotation), ) + # yields ---- + + @dispatch + def render(self, el: ds.DocstringSectionYields): + rows = list(map(self.render, el.value)) + header = ["Name", "Type", "Description"] + + return self._render_table(rows, header, "returns") + + @dispatch + def render(self, el: ds.DocstringYield) -> ParamRow: + # similar to DocstringReturn, but for yields + return ParamRow( + el.name, + el.description, + annotation=self.render_annotation(el.annotation), + ) + # unsupported parts ---- @dispatch.multi( (ds.DocstringAdmonition,), (ds.DocstringDeprecated,), (ds.DocstringWarn,), - (ds.DocstringYield,), (ds.DocstringReceive,), (ds.DocstringAttribute,), ) diff --git a/quartodoc/tests/__snapshots__/test_renderers.ambr b/quartodoc/tests/__snapshots__/test_renderers.ambr index 518827a..543ebc0 100644 --- a/quartodoc/tests/__snapshots__/test_renderers.ambr +++ b/quartodoc/tests/__snapshots__/test_renderers.ambr @@ -447,6 +447,108 @@ | b | str | The b parameter. | _required_ | ''' # --- +# name: test_render_google_section_yields[int: A description.] + ''' + Code + Args: + int: A description. + + Yields: + int: A description. + + Attributes: + int: A description. + + Default + # Parameters {.doc-section .doc-section-parameters} + + | Name | Type | Description | Default | + |--------|--------|----------------|------------| + | int | | A description. | _required_ | + + # Yields {.doc-section .doc-section-yields} + + | Name | Type | Description | + |--------|--------|----------------| + | int | | A description. | + + # Attributes {.doc-section .doc-section-attributes} + + | Name | Type | Description | + |--------|--------|----------------| + | int | | A description. | + + List + # Parameters {.doc-section .doc-section-parameters} + + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + + : A description. + + # Yields {.doc-section .doc-section-yields} + + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + + : A description. + + # Attributes {.doc-section .doc-section-attributes} + + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + + : A description. + ''' +# --- +# name: test_render_google_section_yields[name (int): A description.] + ''' + Code + Args: + name (int): A description. + + Yields: + name (int): A description. + + Attributes: + name (int): A description. + + Default + # Parameters {.doc-section .doc-section-parameters} + + | Name | Type | Description | Default | + |--------|--------|----------------|------------| + | name | int | A description. | _required_ | + + # Yields {.doc-section .doc-section-yields} + + | Name | Type | Description | + |--------|--------|----------------| + | name | int | A description. | + + # Attributes {.doc-section .doc-section-attributes} + + | Name | Type | Description | + |--------|--------|----------------| + | name | int | A description. | + + List + # Parameters {.doc-section .doc-section-parameters} + + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : A description. + + # Yields {.doc-section .doc-section-yields} + + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : A description. + + # Attributes {.doc-section .doc-section-attributes} + + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : A description. + ''' +# --- # name: test_render_numpydoc_section_return[int\n A description.] ''' Code @@ -561,3 +663,195 @@ : A `"description"`. ''' # --- +# name: test_render_numpydoc_section_yields[err_code : int\n Non-zero value indicates error code, or zero on success.\nerr_msg : str or None\n Human readable error message, or None on success.] + ''' + Code + Parameters + --- + err_code : int + Non-zero value indicates error code, or zero on success. + err_msg : str or None + Human readable error message, or None on success. + + Yields + --- + err_code : int + Non-zero value indicates error code, or zero on success. + err_msg : str or None + Human readable error message, or None on success. + + Attributes + --- + err_code : int + Non-zero value indicates error code, or zero on success. + err_msg : str or None + Human readable error message, or None on success. + + Default + # Parameters {.doc-section .doc-section-parameters} + + | Name | Type | Description | Default | + |----------|-------------|----------------------------------------------------------|------------| + | err_code | int | Non-zero value indicates error code, or zero on success. | _required_ | + | err_msg | str or None | Human readable error message, or None on success. | _required_ | + + # Yields {.doc-section .doc-section-yields} + + | Name | Type | Description | + |----------|-------------|----------------------------------------------------------| + | err_code | int | Non-zero value indicates error code, or zero on success. | + | err_msg | str or None | Human readable error message, or None on success. | + + # Attributes {.doc-section .doc-section-attributes} + + | Name | Type | Description | + |----------|-------------|----------------------------------------------------------| + | err_code | int | Non-zero value indicates error code, or zero on success. | + | err_msg | str or None | Human readable error message, or None on success. | + + List + # Parameters {.doc-section .doc-section-parameters} + + [**err_code**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : Non-zero value indicates error code, or zero on success. + + [**err_msg**]{.parameter-name} [:]{.parameter-annotation-sep} [str or None]{.parameter-annotation} + + : Human readable error message, or None on success. + + # Yields {.doc-section .doc-section-yields} + + [**err_code**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : Non-zero value indicates error code, or zero on success. + + [**err_msg**]{.parameter-name} [:]{.parameter-annotation-sep} [str or None]{.parameter-annotation} + + : Human readable error message, or None on success. + + # Attributes {.doc-section .doc-section-attributes} + + [**err_code**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : Non-zero value indicates error code, or zero on success. + + [**err_msg**]{.parameter-name} [:]{.parameter-annotation-sep} [str or None]{.parameter-annotation} + + : Human readable error message, or None on success. + ''' +# --- +# name: test_render_numpydoc_section_yields[int\n A description.] + ''' + Code + Parameters + --- + int + A description. + + Yields + --- + int + A description. + + Attributes + --- + int + A description. + + Default + # Parameters {.doc-section .doc-section-parameters} + + | Name | Type | Description | Default | + |--------|--------|----------------|------------| + | int | | A description. | _required_ | + + # Yields {.doc-section .doc-section-yields} + + | Name | Type | Description | + |--------|--------|----------------| + | | int | A description. | + + # Attributes {.doc-section .doc-section-attributes} + + | Name | Type | Description | + |--------|--------|----------------| + | int | | A description. | + + List + # Parameters {.doc-section .doc-section-parameters} + + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + + : A description. + + # Yields {.doc-section .doc-section-yields} + + []{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : A description. + + # Attributes {.doc-section .doc-section-attributes} + + [**int**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + + : A description. + ''' +# --- +# name: test_render_numpydoc_section_yields[name: int\n A `"description"`.] + ''' + Code + Parameters + --- + name: int + A `"description"`. + + Yields + --- + name: int + A `"description"`. + + Attributes + --- + name: int + A `"description"`. + + Default + # Parameters {.doc-section .doc-section-parameters} + + | Name | Type | Description | Default | + |--------|--------|--------------------|------------| + | name | | A `"description"`. | _required_ | + + # Yields {.doc-section .doc-section-yields} + + | Name | Type | Description | + |--------|--------|--------------------| + | name | int | A `"description"`. | + + # Attributes {.doc-section .doc-section-attributes} + + | Name | Type | Description | + |--------|--------|--------------------| + | name | int | A `"description"`. | + + List + # Parameters {.doc-section .doc-section-parameters} + + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} []{.parameter-annotation} + + : A `"description"`. + + # Yields {.doc-section .doc-section-yields} + + [**name**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation} + + : A `"description"`. + + # Attributes {.doc-section .doc-section-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 ee241f2..ebd310c 100644 --- a/quartodoc/tests/test_renderers.py +++ b/quartodoc/tests/test_renderers.py @@ -247,3 +247,62 @@ def test_render_numpydoc_section_return(snapshot, doc): assert snapshot == indented_sections( Code=full_doc, Default=res_default, List=res_list ) + + +@pytest.mark.parametrize( + "doc", + [ + """name: int\n A `"description"`.""", + """int\n A description.""", + """err_code : int\n Non-zero value indicates error code, or zero on success.\nerr_msg : str or None\n Human readable error message, or None on success.""", + ], +) +def test_render_numpydoc_section_yields(snapshot, doc): + from quartodoc.parsers import get_parser_defaults + from griffe import Parser + + full_doc = ( + f"""Parameters\n---\n{doc}\n\nYields\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 + ) + + +@pytest.mark.parametrize( + "doc", + [ + """name (int): A description.""", + """int: A description.""", + ], +) +def test_render_google_section_yields(snapshot, doc): + from quartodoc.parsers import get_parser_defaults + from griffe import Parser + + full_doc = f"""Args:\n {doc}\n\nYields:\n {doc}\n\nAttributes:\n {doc}""" + + el = dc.Docstring( + value=full_doc, + parser=Parser.google, + parser_options=get_parser_defaults("google"), + ) + + 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 + )