Skip to content

Commit 8c7207d

Browse files
authored
Build API docs (#633)
1 parent 031bd89 commit 8c7207d

File tree

15 files changed

+1098
-0
lines changed

15 files changed

+1098
-0
lines changed

.github/workflows/build-docs.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Build API docs
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches: ["main"]
7+
pull_request:
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.11"]
15+
fail-fast: false
16+
17+
steps:
18+
- uses: actions/checkout@v3
19+
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v4
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Upgrade pip
26+
run: python -m pip install --upgrade pip
27+
28+
- name: Install Quarto
29+
uses: quarto-dev/quarto-actions/setup@v2
30+
with:
31+
version: 1.3.340
32+
33+
- name: Install dependencies
34+
run: |
35+
cd docs
36+
make deps
37+
38+
- name: Run quartodoc
39+
run: |
40+
cd docs
41+
make quartodoc
42+
43+
- name: Build site
44+
run: |
45+
cd docs
46+
make site

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,5 @@ docs/source/reference/
107107

108108
.DS_Store
109109
.Rproj.user
110+
111+
/.luarc.json

docs/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
_site/
2+
api/
3+
_inv/
4+
_sidebar.yml
5+
/.quarto/
6+
objects.json

docs/Makefile

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
.PHONY: help Makefile
2+
.DEFAULT_GOAL := help
3+
4+
define BROWSER_PYSCRIPT
5+
import os, webbrowser, sys
6+
7+
from urllib.request import pathname2url
8+
9+
webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
10+
endef
11+
export BROWSER_PYSCRIPT
12+
13+
define PRINT_HELP_PYSCRIPT
14+
import re, sys
15+
16+
for line in sys.stdin:
17+
match = re.match(r'^([a-zA-Z1-9_-]+):.*?## (.*)$$', line)
18+
if match:
19+
target, help = match.groups()
20+
print("%-20s %s" % (target, help))
21+
endef
22+
export PRINT_HELP_PYSCRIPT
23+
24+
BROWSER := python -c "$$BROWSER_PYSCRIPT"
25+
26+
# Use venv from parent
27+
VENV = ../venv
28+
PYBIN = $(VENV)/bin
29+
30+
# Any targets that depend on $(VENV) or $(PYBIN) will cause the venv to be
31+
# created. To use the venv, python scripts should run with the prefix $(PYBIN),
32+
# as in `$(PYBIN)/pip`.
33+
$(VENV):
34+
python3 -m venv $(VENV)
35+
36+
$(PYBIN): $(VENV)
37+
38+
39+
help:
40+
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
41+
42+
deps: $(PYBIN) ## Install build dependencies
43+
$(PYBIN)/pip install pip --upgrade
44+
$(PYBIN)/pip install -e ..[doc]
45+
46+
quartodoc: ## Build qmd files for API docs
47+
. $(PYBIN)/activate \
48+
&& quartodoc interlinks \
49+
&& quartodoc build --config _quartodoc.yml --verbose
50+
51+
site: ## Build website
52+
. $(PYBIN)/activate \
53+
&& quarto render
54+
55+
serve: ## Build website and serve
56+
. $(PYBIN)/activate \
57+
&& quarto preview --port 8080
58+
59+
clean: ## Clean build artifacts
60+
rm -rf _inv api _site .quarto

docs/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
Shiny for Python API docs
2+
=========================
3+
4+
This directory contains files to generate Shiny for Python API documentation, using [Quartodoc](https://machow.github.io/quartodoc/get-started/overview.html) and [Quarto](https://quarto.org/).
5+
6+
## Building the docs
7+
8+
To build the docs, first install the Python dependencies and Quarto extensions:
9+
10+
```bash
11+
# Install build dependencies
12+
make py-deps
13+
```
14+
15+
After those dependencies are installed, build the .qmd files for Shiny, using quartodoc. This will go in the `api/` directory:
16+
17+
```bash
18+
make quartodoc
19+
```
20+
21+
Then build the web site using Quarto:
22+
23+
```bash
24+
make site
25+
```
26+
27+
Alternatively, running `make serve` will build the docs, and serve them locally, and watch for changes to the .qmd files:
28+
29+
```bash
30+
make serve
31+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.html
2+
*.pdf
3+
*_files/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
title: Interlinks
2+
author: Michael Chow
3+
version: 1.0.0
4+
quarto-required: ">=1.2.0"
5+
contributes:
6+
filters:
7+
- interlinks.lua
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
local function read_json(filename)
2+
local file = io.open(filename, "r")
3+
if file == nil then
4+
return nil
5+
end
6+
local str = file:read("a")
7+
file:close()
8+
return quarto.json.decode(str)
9+
end
10+
11+
local inventory = {}
12+
13+
function lookup(search_object)
14+
15+
local results = {}
16+
for ii, inventory in ipairs(inventory) do
17+
for jj, item in ipairs(inventory.items) do
18+
-- e.g. :external+<inv_name>:<domain>:<role>:`<name>`
19+
if item.inv_name and item.inv_name ~= search_object.inv_name then
20+
goto continue
21+
end
22+
23+
if item.name ~= search_object.name then
24+
goto continue
25+
end
26+
27+
if search_object.role and item.role ~= search_object.role then
28+
goto continue
29+
end
30+
31+
if search_object.domain and item.domain ~= search_object.domain then
32+
goto continue
33+
else
34+
table.insert(results, item)
35+
36+
goto continue
37+
end
38+
39+
::continue::
40+
end
41+
end
42+
43+
if #results == 1 then
44+
return results[1]
45+
end
46+
if #results > 1 then
47+
print("Found multiple matches for " .. search_object.name)
48+
quarto.utils.dump(results)
49+
return nil
50+
end
51+
if #results == 0 then
52+
print("Found no matches for object:")
53+
quarto.utils.dump(search_object)
54+
end
55+
56+
return nil
57+
end
58+
59+
function mysplit (inputstr, sep)
60+
if sep == nil then
61+
sep = "%s"
62+
end
63+
local t={}
64+
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
65+
table.insert(t, str)
66+
end
67+
return t
68+
end
69+
70+
local function normalize_role(role)
71+
if role == "func" then
72+
return "function"
73+
end
74+
return role
75+
end
76+
77+
local function build_search_object(str)
78+
local starts_with_colon = str:sub(1, 1) == ":"
79+
local search = {}
80+
if starts_with_colon then
81+
local t = mysplit(str, ":")
82+
if #t == 2 then
83+
-- e.g. :py:func:`my_func`
84+
search.role = normalize_role(t[1])
85+
search.name = t[2]:match("%%60(.*)%%60")
86+
elseif #t == 3 then
87+
-- e.g. :py:func:`my_func`
88+
search.domain = t[1]
89+
search.role = normalize_role(t[2])
90+
search.name = t[3]:match("%%60(.*)%%60")
91+
elseif #t == 4 then
92+
-- e.g. :ext+inv:py:func:`my_func`
93+
search.external = true
94+
95+
search.inv_name = t[1]:match("external%+(.*)")
96+
search.domain = t[2]
97+
search.role = normalize_role(t[3])
98+
search.name = t[4]:match("%%60(.*)%%60")
99+
else
100+
print("couldn't parse this link: " .. str)
101+
return {}
102+
end
103+
else
104+
search.name = str:match("%%60(.*)%%60")
105+
end
106+
107+
if search.name == nil then
108+
print("couldn't parse this link: " .. str)
109+
return {}
110+
end
111+
112+
if search.name:sub(1, 1) == "~" then
113+
search.shortened = true
114+
search.name = search.name:sub(2, -1)
115+
end
116+
return search
117+
end
118+
119+
function report_broken_link(link, search_object, replacement)
120+
-- TODO: how to unescape html elements like [?
121+
return pandoc.Code(pandoc.utils.stringify(link.content))
122+
end
123+
124+
function Link(link)
125+
-- do not process regular links ----
126+
if not link.target:match("%%60") then
127+
return link
128+
end
129+
130+
-- lookup item ----
131+
local search = build_search_object(link.target)
132+
local item = lookup(search)
133+
134+
-- determine replacement, used if no link text specified ----
135+
local original_text = pandoc.utils.stringify(link.content)
136+
local replacement = search.name
137+
if search.shortened then
138+
local t = mysplit(search.name, ".")
139+
replacement = t[#t]
140+
end
141+
142+
-- set link text ----
143+
if original_text == "" and replacement ~= nil then
144+
link.content = pandoc.Code(replacement)
145+
end
146+
147+
-- report broken links ----
148+
if item == nil then
149+
return report_broken_link(link, search)
150+
end
151+
link.target = item.uri:gsub("%$$", search.name)
152+
153+
154+
return link
155+
end
156+
157+
function fixup_json(json, prefix)
158+
for _, item in ipairs(json.items) do
159+
item.uri = prefix .. item.uri
160+
end
161+
table.insert(inventory, json)
162+
end
163+
164+
return {
165+
{
166+
Meta = function(meta)
167+
local json
168+
local prefix
169+
for k, v in pairs(meta.interlinks.sources) do
170+
json = read_json(quarto.project.offset .. "/_inv/" .. k .. "_objects.json")
171+
prefix = pandoc.utils.stringify(v.url)
172+
fixup_json(json, prefix)
173+
end
174+
json = read_json(quarto.project.offset .. "/objects.json")
175+
if json ~= nil then
176+
fixup_json(json, "/")
177+
end
178+
end
179+
},
180+
{
181+
Link = Link
182+
}
183+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: shinylive
2+
title: Embedded Shinylive applications
3+
author: Winston Chang
4+
version: 0.0.3
5+
quarto-required: ">=1.2.198"
6+
contributes:
7+
filters:
8+
- shinylive.lua
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
div.output-content,
2+
div.shinylive-wrapper {
3+
background-color: rgba(250, 250, 250, 0.65);
4+
border: 1px solid rgba(233, 236, 239, 0.65);
5+
border-radius: 0.5rem;
6+
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.04), 0px 3px 7px rgba(0, 0, 0, 0.04),
7+
0px 12px 30px rgba(0, 0, 0, 0.07);
8+
margin-top: 32px;
9+
margin-bottom: 32px;
10+
}
11+
12+
div.shinylive-wrapper {
13+
margin: 1em 0;
14+
border-radius: 8px;
15+
}
16+
17+
.shinylive-container {
18+
background-color: #eeeff2;
19+
min-height: auto;
20+
}
21+
22+
.shinylive-container > div {
23+
box-shadow: none;
24+
}
25+
26+
.editor-container .cm-editor .cm-scroller {
27+
font-size: 13px;
28+
line-height: 1.5;
29+
}
30+
31+
iframe.app-frame {
32+
/* Override the default margin from Bootstrap */
33+
margin-bottom: 0;
34+
}

0 commit comments

Comments
 (0)