diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 80b1d0d7..ec41e4e9 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -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 diff --git a/quartodoc/builder/blueprint.py b/quartodoc/builder/blueprint.py index 695da8ab..de5cd489 100644 --- a/quartodoc/builder/blueprint.py +++ b/quartodoc/builder/blueprint.py @@ -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 diff --git a/quartodoc/layout.py b/quartodoc/layout.py index d10f4555..77970eb3 100644 --- a/quartodoc/layout.py +++ b/quartodoc/layout.py @@ -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 @@ -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: @@ -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 @@ -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 = [] @@ -362,6 +367,7 @@ def from_griffe( "obj": obj, "anchor": anchor, "signature_path": signature_path, + "show_source": show_source, } if kind == "function": diff --git a/quartodoc/renderers/base.py b/quartodoc/renderers/base.py index 8802b299..c1467282 100644 --- a/quartodoc/renderers/base.py +++ b/quartodoc/renderers/base.py @@ -1,4 +1,5 @@ import re +import html from plum import dispatch @@ -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("|", "\\|") diff --git a/quartodoc/renderers/md_renderer.py b/quartodoc/renderers/md_renderer.py index 582396ef..b8d61e4c 100644 --- a/quartodoc/renderers/md_renderer.py +++ b/quartodoc/renderers/md_renderer.py @@ -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: @@ -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"
{code}
"
+
+ # 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"def f_quarto_block_in_docstring(a, b: str):
+ """A quarto style docstring.
+
+ ```{python}
+ 1 < 2
+ ```
+ """
+ {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