Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ quartodoc:
These functions fetch and analyze python objects, including parsing docstrings.
They prepare a basic representation of your doc site that can be rendered and built.
contents:
- Auto
- name: Auto
show_source: true
- blueprint
- collect
- get_object
Expand Down
7 changes: 6 additions & 1 deletion quartodoc/builder/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,12 @@ def enter(self, el: Auto):

is_flat = el.children == ChoicesChildren.flat
return Doc.from_griffe(
el.name, obj, children, flat=is_flat, signature_path=el.signature_path
el.name,
obj,
children,
flat=is_flat,
signature_path=el.signature_path,
show_source=el.show_source,
)

@staticmethod
Expand Down
6 changes: 6 additions & 0 deletions quartodoc/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class AutoOptions(_Base):
exclude: Optional[str] = None
dynamic: Union[None, bool, str] = None
children: ChoicesChildren = ChoicesChildren.embedded
show_source: bool = False
package: Union[str, None, MISSING] = MISSING()
member_options: Optional["AutoOptions"] = None

Expand Down Expand Up @@ -270,6 +271,8 @@ class Auto(AutoOptions):
to return an alias for that object.
children:
Style for presenting members. Either separate, embedded, or flat.
show_source:
Whether to show the source code for the object.
package:
If specified, object lookup will be relative to this path.
member_options:
Expand Down Expand Up @@ -336,6 +339,7 @@ class Doc(_Docable):
obj: Union[dc.Object, dc.Alias]
anchor: str
signature_path: SignatureOptions = "relative"
show_source: bool = False

class Config:
arbitrary_types_allowed = True
Expand All @@ -350,6 +354,7 @@ def from_griffe(
anchor: str = None,
flat: bool = False,
signature_path: str = "relative",
show_source: bool = False,
):
if members is None:
members = []
Expand All @@ -362,6 +367,7 @@ def from_griffe(
"obj": obj,
"anchor": anchor,
"signature_path": signature_path,
"show_source": show_source,
}

if kind == "function":
Expand Down
13 changes: 13 additions & 0 deletions quartodoc/renderers/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import html

from plum import dispatch

Expand All @@ -10,6 +11,18 @@ def escape(val: str):
return f"`{val}`"


def escape_source(source: str):
"""Escape python source code for adding to a qmd file.

Since quarto looks for ```{python} blocks, even in indented markdown,
we need to be careful to ensure it doesn't attempt to execute parts of
python source code (since the output will look wrong and weird).
"""

# escape html characters, and replace ` with its html encoding
return html.escape(source, quote=False).replace("`", "`")


def sanitize(val: str, allow_markdown=False):
# sanitize common tokens that break tables
res = val.replace("\n", " ").replace("|", "\\|")
Expand Down
27 changes: 22 additions & 5 deletions quartodoc/renderers/md_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Tuple, Union, Optional
from quartodoc import layout

from .base import Renderer, escape, sanitize, convert_rst_link_to_md
from .base import Renderer, escape, escape_source, sanitize, convert_rst_link_to_md


try:
Expand Down Expand Up @@ -176,6 +176,22 @@ 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}"

# render source -----------------------------------------------------------

@dispatch
def render_source(self, el: Union[dc.Object, dc.Alias]):
"""Render the source code for an object."""

# TODO: what happens if no source
code = escape_source(el.source)

code_block = f"<pre><code>{code}</code></pre>"

# TODO: could use el.relative_filepath to show file path, but
# this does it relative to the current working directory, so if
# your cwd is a docs folder, surprising things can happen.
return f"<details>\n<summary>Show source</summary>\n{code_block}\n</details>"

# render method -----------------------------------------------------------

Expand Down Expand Up @@ -330,20 +346,21 @@ def render(self, el: Union[layout.DocClass, layout.DocModule]):

str_sig = self.signature(el)
sig_part = [str_sig] if self.show_signature else []
source = [self.render_source(el.obj)] if el.show_source else []

body = self.render(el.obj)


return "\n\n".join([title, *sig_part, body, *attr_docs, *class_docs, *meth_docs])
return "\n\n".join([title, *sig_part, body, *source, *attr_docs, *class_docs, *meth_docs])

@dispatch
def render(self, el: Union[layout.DocFunction, layout.DocAttribute]):
title = self.render_header(el)

str_sig = self.signature(el)
sig_part = [str_sig] if self.show_signature else []
sig_part = [self.signature(el)] if self.show_signature else []
source = [self.render_source(el.obj)] if el.show_source else []

return "\n\n".join([title, *sig_part, self.render(el.obj)])
return "\n\n".join([title, *sig_part, self.render(el.obj), *source])

# render griffe objects ===================================================

Expand Down
24 changes: 24 additions & 0 deletions quartodoc/tests/__snapshots__/test_renderers.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,30 @@
A function
'''
# ---
# name: test_render_doc_show_source
'''
# f_quarto_block_in_docstring { #quartodoc.tests.example_docstring_styles.f_quarto_block_in_docstring }

`tests.example_docstring_styles.f_quarto_block_in_docstring(a, b)`

A quarto style docstring.

```{python}
1 < 2
```

<details>
<summary>Show source</summary>
<pre><code>def f_quarto_block_in_docstring(a, b: str):
"""A quarto style docstring.

&#96;&#96;&#96;{python}
1 &lt; 2
&#96;&#96;&#96;
"""</code></pre>
</details>
'''
# ---
# name: test_render_doc_signature_path
'''
# example.a_func { #quartodoc.tests.example.a_func }
Expand Down
9 changes: 9 additions & 0 deletions quartodoc/tests/example_docstring_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,12 @@ def f_numpy_with_linebreaks(a, b: str):
The b parameter.

"""


def f_quarto_block_in_docstring(a, b: str):
"""A quarto style docstring.

```{python}
1 < 2
```
"""
22 changes: 22 additions & 0 deletions quartodoc/tests/test_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,25 @@ def test_render_doc_signature_path(snapshot, renderer):
res = renderer.render(bp)

assert res == snapshot


def test_render_source(renderer):
obj = get_object("quartodoc.tests.example.a_func")
res = renderer.render_source(obj)

code = '''\
def a_func():
"""A function"""\
'''

res == f"<code><pre>{code}</pre></code>"


def test_render_doc_show_source(renderer, snapshot):
package = "quartodoc.tests.example_docstring_styles"
auto = Auto(name="f_quarto_block_in_docstring", package=package, show_source=True)
bp = blueprint(auto)

res = renderer.render(bp)

assert res == snapshot