The big picture: Builder

While the “Basic Use” section covered how to configure and build a site with quartodoc, this section focuses on using quartodoc as a Python program.

Programming with quartodoc will help with debugging, tinkering, and extending things.

Overview

When a user runs quartodoc build, they

  • Create a Builder object, with the quartodoc config loaded as a layout.Layout.
  • Use blueprint to process the layout into a plan for building the website.
  • Use collect to get pages to render, and info on where documented objects live.

This page will cover the basics of the Builder and this process.

The Builder

The code below shows a Builder object being loaded from a _quarto.yml config (loaded as a Python dictionary).

import yaml

from quartodoc import Builder, blueprint, collect, MdRenderer

cfg = yaml.safe_load("""
quartodoc:
  package: quartodoc
  style: pkgdown
  sections:
    - title: "Some section"
      desc: "Some description"
      contents:
        - name: MdRenderer
          members: ["render", "summarize"]
          children: separate
""")

builder = Builder.from_quarto_config(cfg)
builder
<quartodoc.autosummary.BuilderPkgdown at 0x7fe0189fe5f0>

Note that .from_quarto_config used the style: field to decide which Builder to create (in this case, BuilderPkgdown).

We can view the config as a layout.Layout, by looking at the .layout attribute.

builder.layout
Layout(sections=[Section(kind='section', title='Some section', subtitle=None, desc='Some description', package=MISSING(), contents=[Auto(signature_name='relative', members=['render', 'summarize'], include_private=False, include_imports=False, include_empty=False, include_inherited=False, include_attributes=True, include_classes=True, include_functions=True, include=None, exclude=None, dynamic=None, children=<ChoicesChildren.separate: 'separate'>, package=MISSING(), member_options=None, kind='auto', name='MdRenderer')], options=None)], package='quartodoc', options=None)

This can be a bit difficult to read, so quartodoc implements a preview function, which spaces things out.

from quartodoc import preview
preview(builder.layout)
█─Layout
├─sections = █─list
│            └─0 = █─Section
│                  ├─title = 'Some section'
│                  ├─desc = 'Some description'
│                  └─contents = █─list
│                               └─0 = █─Auto
│                                     ├─members = █─list
│                                     │           ├─0 = 'render'
│                                     │           └─1 = 'summarize'
│                                     ├─children = <ChoicesChildren.separate: 'separate'>
│                                     └─name = 'MdRenderer'
└─package = 'quartodoc'

Notice the following:

  • preview represents calls like Layout() with a box to the left, and then a pipe connecting it to each of its arguments.
  • The content entry MdRenderer is represented by an Auto class. This specifies a Python object to look up and document.

We can follow the path in the preview above, to pull out just this first piece of content containing MdRenderer:

content = builder.layout.sections[0].contents[0]
preview(content)
█─Auto
├─members = █─list
│           ├─0 = 'render'
│           └─1 = 'summarize'
├─children = <ChoicesChildren.separate: 'separate'>
└─name = 'MdRenderer'

Next, we’ll look at blueprint(), which processes the layout, including transforming Auto objects (like the one representing the MdRenderer above) into more concrete instructions.

From config to blueprint

The code below shows how blueprint() transforms the Auto entry for MdRenderer.

bp = blueprint(builder.layout)
bp_contents = bp.sections[0].contents[0]
preview(bp_contents, max_depth=3)
█─Page
├─path = 'MdRenderer'
└─contents = █─list
             └─0 = █─DocClass
                   ├─name = 'MdRenderer'
                   ├─obj = █─Alias ...
                   ├─anchor = 'quartodoc.MdRenderer'
                   ├─members = █─list ...
                   └─flat = False

Notice two key pieces:

  • The Auto element is now a layout.Page. The .path indicates that the documentation will be on a page called "MdRenderer".
  • The content of the page is a layout.DocClass. This element holds everything needed to render this doc, including the class signature and parsed docstring.

Importantly, the .members attribute stores how to render the class methods we listed in our configuration yaml, .render() and .summarize():

preview(bp_contents.contents[0].members, max_depth=2)
█─list
├─0 = █─MemberPage
│     ├─path = 'quartodoc.MdRenderer.render'
│     └─contents = █─list ...
└─1 = █─MemberPage
      ├─path = 'quartodoc.MdRenderer.summarize'
      └─contents = █─list ...

Note that they are also a instances of Page (MemberPage to be exact). Before to building the site, we need to collect() all the pages.

Collecting pages and items

The collect function pulls out two important pieces of information:

  • pages - each page to be rendered.
  • items - information on where each documented object lives in the site, which is used for things like interlinks.
pages, items = collect(bp, builder.dir)
preview(pages, max_depth=3)
█─list
├─0 = █─MemberPage
│     ├─path = 'quartodoc.MdRenderer.render'
│     └─contents = █─list
│                  └─0 = █─DocFunction ...
├─1 = █─MemberPage
│     ├─path = 'quartodoc.MdRenderer.summarize'
│     └─contents = █─list
│                  └─0 = █─DocFunction ...
└─2 = █─Page
      ├─path = 'MdRenderer'
      └─contents = █─list
                   └─0 = █─DocClass ...

The code below shows a preview of the items.

preview(items, max_depth=2)
█─list
├─0 = █─Item
│     ├─name = 'quartodoc.MdRenderer.render'
│     ├─obj = █─Alias ...
│     └─uri = 'reference/quartodoc.MdRenderer.render.html#quarto ...
├─1 = █─Item
│     ├─name = 'quartodoc.renderers.md_renderer.MdRenderer.render ...
│     ├─obj = █─Alias ...
│     ├─uri = 'reference/quartodoc.MdRenderer.render.html#quarto ...
│     └─dispname = 'quartodoc.MdRenderer.render'
├─2 = █─Item
│     ├─name = 'quartodoc.MdRenderer.summarize'
│     ├─obj = █─Alias ...
│     └─uri = 'reference/quartodoc.MdRenderer.summarize.html#qua ...
├─3 = █─Item
│     ├─name = 'quartodoc.renderers.md_renderer.MdRenderer.summar ...
│     ├─obj = █─Alias ...
│     ├─uri = 'reference/quartodoc.MdRenderer.summarize.html#qua ...
│     └─dispname = 'quartodoc.MdRenderer.summarize'
├─4 = █─Item
│     ├─name = 'quartodoc.MdRenderer'
│     ├─obj = █─Alias ...
│     └─uri = 'reference/MdRenderer.html#quartodoc.MdRenderer'
└─5 = █─Item
      ├─name = 'quartodoc.renderers.md_renderer.MdRenderer'
      ├─obj = █─Alias ...
      ├─uri = 'reference/MdRenderer.html#quartodoc.MdRenderer'
      └─dispname = 'quartodoc.MdRenderer'

Notice that if you wanted to look up quartodoc.MdRenderer.render, the first item’s .uri attribute shows the URL for it, relative to wherever the doc site is hosted.

Rendering and writing

A Builder instantiates a Renderer (like MdRenderer). Use the .renderer attribute to access it:

builder.renderer
<quartodoc.renderers.md_renderer.MdRenderer at 0x7fe0189cdea0>

The render method of of the MdRenderer returns a markdown string that can be rendered by Quarto:

print(builder.renderer.render(pages[0]))
# render { #quartodoc.MdRenderer.render }

`MdRenderer.render(el)`

Cross References

The { #quartodoc.MdRenderer.render } in the output above is extended Quarto markdown that is a cross reference.

Writing pages

The builder has a number of methods it uses while materializing files that will be rendered by Quarto. The main method is .build(). See the Builder section of the API for a list of methods, or this giant build process diagram for a full breakdown.