Skip to content

Commit 1fc3fbd

Browse files
authored
feat: simplify return table and list if possible (#416)
* feat: simplify return table and list if possible * add missing example module
1 parent 8d8252f commit 1fc3fbd

File tree

4 files changed

+293
-9
lines changed

4 files changed

+293
-9
lines changed

quartodoc/renderers/md_renderer.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,16 @@ def to_definition_list(self):
8484

8585
part_desc = desc if desc is not None else ""
8686

87-
anno_sep = Span(":", Attr(classes=["parameter-annotation-sep"]))
87+
# Only include the colon separator if there's a name
88+
if name is not None:
89+
anno_sep = Span(":", Attr(classes=["parameter-annotation-sep"]))
90+
parts = [part_name, anno_sep, part_anno, part_default_sep, part_default]
91+
else:
92+
# No name means no colon separator (e.g., for Raises)
93+
parts = [part_anno, part_default_sep, part_default]
8894

8995
# TODO: should code wrap the whole thing like this?
90-
param = Code(
91-
str(
92-
Inlines(
93-
[part_name, anno_sep, part_anno, part_default_sep, part_default]
94-
)
95-
)
96-
).html
96+
param = Code(str(Inlines(parts))).html
9797
return (param, part_desc)
9898

9999
def to_tuple(self, style: Literal["parameters", "attributes", "returns"]):
@@ -265,7 +265,21 @@ def _render_table(
265265
if self.table_style == "description-list":
266266
return str(DefinitionList([row.to_definition_list() for row in rows]))
267267
else:
268-
row_tuples = [row.to_tuple(style) for row in rows]
268+
# Check if any rows have names - if not, omit the Name column
269+
# Note: Parameters always have names, but Returns/Raises may not
270+
has_names = any(row.name is not None for row in rows)
271+
272+
if not has_names and style == "returns" and headers[0] == "Name":
273+
# Omit Name column when no items have names (e.g., Raises section)
274+
headers = headers[1:] # Remove "Name" from headers
275+
row_tuples = [
276+
(row.annotation, sanitize(row.description, allow_markdown=True))
277+
for row in rows
278+
]
279+
else:
280+
# Standard rendering with all columns
281+
row_tuples = [row.to_tuple(style) for row in rows]
282+
269283
table = tabulate(row_tuples, headers=headers, tablefmt="github")
270284
return table
271285

@@ -766,6 +780,7 @@ def render(self, el: Union[ds.DocstringSectionReturns, ds.DocstringSectionRaises
766780
rows = list(map(self.render, el.value))
767781
header = ["Name", "Type", "Description"]
768782

783+
# _render_table will dynamically omit the Name column if no rows have names
769784
return self._render_table(rows, header, "returns")
770785

771786
@dispatch

quartodoc/tests/__snapshots__/test_renderers.ambr

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,184 @@
579579
| b | str | The b parameter. | _required_ |
580580
'''
581581
# ---
582+
# name: test_render_full_numpydoc
583+
'''
584+
# full_numpydoc_function { #quartodoc.tests.example_docstring_full.full_numpydoc_function }
585+
586+
```python
587+
tests.example_docstring_full.full_numpydoc_function(
588+
x,
589+
y=None,
590+
*args,
591+
option=False,
592+
**kwargs,
593+
)
594+
```
595+
596+
A one-line summary.
597+
598+
An extended summary that provides more detail about what this
599+
function does. This demonstrates the full range of numpydoc
600+
sections that are supported.
601+
602+
## Parameters {.doc-section .doc-section-parameters}
603+
604+
| Name | Type | Description | Default |
605+
|----------|--------|--------------------------------|------------|
606+
| x | int | The first parameter. | _required_ |
607+
| y | str | The second parameter. | `None` |
608+
| *args | float | Variable positional arguments. | `()` |
609+
| option | bool | A keyword-only parameter. | `False` |
610+
| **kwargs | dict | Variable keyword arguments. | `{}` |
611+
612+
## Returns {.doc-section .doc-section-returns}
613+
614+
| Name | Type | Description |
615+
|--------|--------|------------------------------------------|
616+
| result | int | The computed result with name and type. |
617+
| | list | A secondary return value with only type. |
618+
619+
## Yields {.doc-section .doc-section-yields}
620+
621+
| Name | Type | Description |
622+
|--------|--------|--------------------------|
623+
| value | str | Generated string values. |
624+
625+
## Raises {.doc-section .doc-section-raises}
626+
627+
| Type | Description |
628+
|------------|-----------------------|
629+
| ValueError | If x is negative. |
630+
| TypeError | If y is not a string. |
631+
632+
## See Also {.doc-section .doc-section-see-also}
633+
634+
other_function : Related functionality.
635+
module.another_function : Another related function.
636+
637+
## Notes {.doc-section .doc-section-notes}
638+
639+
I am a note.
640+
641+
## References {.doc-section .doc-section-references}
642+
643+
.. [1] Author Name, "Paper Title", Journal, 2024. TODO
644+
645+
## Examples {.doc-section .doc-section-examples}
646+
647+
Basic usage:
648+
649+
```python
650+
>>> full_numpydoc_function(1, "test")
651+
(42, [1, 2, 3])
652+
```
653+
654+
With optional parameters:
655+
656+
```python
657+
>>> full_numpydoc_function(1, option=True)
658+
(42, [])
659+
```
660+
'''
661+
# ---
662+
# name: test_render_full_numpydoc_description_list
663+
'''
664+
# full_numpydoc_function { #quartodoc.tests.example_docstring_full.full_numpydoc_function }
665+
666+
```python
667+
tests.example_docstring_full.full_numpydoc_function(
668+
x,
669+
y=None,
670+
*args,
671+
option=False,
672+
**kwargs,
673+
)
674+
```
675+
676+
A one-line summary.
677+
678+
An extended summary that provides more detail about what this
679+
function does. This demonstrates the full range of numpydoc
680+
sections that are supported.
681+
682+
## Parameters {.doc-section .doc-section-parameters}
683+
684+
<code>[**x**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation}</code>
685+
686+
: The first parameter.
687+
688+
<code>[**y**]{.parameter-name} [:]{.parameter-annotation-sep} [str]{.parameter-annotation} [ = ]{.parameter-default-sep} [None]{.parameter-default}</code>
689+
690+
: The second parameter.
691+
692+
<code>[***args**]{.parameter-name} [:]{.parameter-annotation-sep} [float]{.parameter-annotation} [ = ]{.parameter-default-sep} [()]{.parameter-default}</code>
693+
694+
: Variable positional arguments.
695+
696+
<code>[**option**]{.parameter-name} [:]{.parameter-annotation-sep} [bool]{.parameter-annotation} [ = ]{.parameter-default-sep} [False]{.parameter-default}</code>
697+
698+
: A keyword-only parameter.
699+
700+
<code>[****kwargs**]{.parameter-name} [:]{.parameter-annotation-sep} [dict]{.parameter-annotation} [ = ]{.parameter-default-sep} [{}]{.parameter-default}</code>
701+
702+
: Variable keyword arguments.
703+
704+
## Returns {.doc-section .doc-section-returns}
705+
706+
<code>[**result**]{.parameter-name} [:]{.parameter-annotation-sep} [int]{.parameter-annotation}</code>
707+
708+
: The computed result with name and type.
709+
710+
<code>[]{.parameter-name} [:]{.parameter-annotation-sep} [list]{.parameter-annotation}</code>
711+
712+
: A secondary return value with only type.
713+
714+
## Yields {.doc-section .doc-section-yields}
715+
716+
<code>[**value**]{.parameter-name} [:]{.parameter-annotation-sep} [str]{.parameter-annotation}</code>
717+
718+
: Generated string values.
719+
720+
## Raises {.doc-section .doc-section-raises}
721+
722+
<code>[ValueError]{.parameter-annotation}</code>
723+
724+
: If x is negative.
725+
726+
<code>[TypeError]{.parameter-annotation}</code>
727+
728+
: If y is not a string.
729+
730+
## See Also {.doc-section .doc-section-see-also}
731+
732+
other_function : Related functionality.
733+
module.another_function : Another related function.
734+
735+
## Notes {.doc-section .doc-section-notes}
736+
737+
I am a note.
738+
739+
## References {.doc-section .doc-section-references}
740+
741+
.. [1] Author Name, "Paper Title", Journal, 2024. TODO
742+
743+
## Examples {.doc-section .doc-section-examples}
744+
745+
Basic usage:
746+
747+
```python
748+
>>> full_numpydoc_function(1, "test")
749+
(42, [1, 2, 3])
750+
```
751+
752+
With optional parameters:
753+
754+
```python
755+
>>> full_numpydoc_function(1, option=True)
756+
(42, [])
757+
```
758+
'''
759+
# ---
582760
# name: test_render_google_section_yields[int: A description.]
583761
'''
584762
Code
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Module with comprehensive numpydoc examples."""
2+
3+
4+
def full_numpydoc_function(x, y=None, *args, option=False, **kwargs):
5+
"""A one-line summary.
6+
7+
An extended summary that provides more detail about what this
8+
function does. This demonstrates the full range of numpydoc
9+
sections that are supported.
10+
11+
Parameters
12+
----------
13+
x : int
14+
The first parameter.
15+
y : str, optional
16+
The second parameter.
17+
*args : float
18+
Variable positional arguments.
19+
option : bool, default False
20+
A keyword-only parameter.
21+
**kwargs : dict
22+
Variable keyword arguments.
23+
24+
Returns
25+
-------
26+
result : int
27+
The computed result with name and type.
28+
list
29+
A secondary return value with only type.
30+
31+
Yields
32+
------
33+
value : str
34+
Generated string values.
35+
36+
Raises
37+
------
38+
ValueError
39+
If x is negative.
40+
TypeError
41+
If y is not a string.
42+
43+
See Also
44+
--------
45+
other_function : Related functionality.
46+
module.another_function : Another related function.
47+
48+
Notes
49+
-----
50+
I am a note.
51+
52+
References
53+
----------
54+
.. [1] Author Name, "Paper Title", Journal, 2024. TODO
55+
56+
Examples
57+
--------
58+
Basic usage:
59+
60+
>>> full_numpydoc_function(1, "test")
61+
(42, [1, 2, 3])
62+
63+
With optional parameters:
64+
65+
>>> full_numpydoc_function(1, option=True)
66+
(42, [])
67+
"""
68+
pass

quartodoc/tests/test_renderers.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,26 @@ def test_render_doc_summarize_toc_table_vs_description_list(snapshot):
409409
)
410410

411411
assert snapshot == indented_sections(Table=res_table, DescriptionList=res_list)
412+
413+
414+
def test_render_full_numpydoc(snapshot, renderer):
415+
"""Test rendering a function with comprehensive numpydoc sections."""
416+
package = "quartodoc.tests.example_docstring_full"
417+
auto = Auto(name="full_numpydoc_function", package=package)
418+
bp = blueprint(auto, parser="numpy")
419+
420+
res = renderer.render(bp)
421+
422+
assert res == snapshot
423+
424+
425+
def test_render_full_numpydoc_description_list(snapshot):
426+
"""Test rendering a function with comprehensive numpydoc sections using description list style."""
427+
renderer = MdRenderer(table_style="description-list")
428+
package = "quartodoc.tests.example_docstring_full"
429+
auto = Auto(name="full_numpydoc_function", package=package)
430+
bp = blueprint(auto, parser="numpy")
431+
432+
res = renderer.render(bp)
433+
434+
assert res == snapshot

0 commit comments

Comments
 (0)