Skip to content

Commit 200f396

Browse files
euri10cofin
authored andcommitted
Add interactive Pyodide playground for SQLSpec documentation
Signed-off-by: euri10 <[email protected]>
1 parent 2cb5dfe commit 200f396

File tree

6 files changed

+126
-0
lines changed

6 files changed

+126
-0
lines changed

docs/_ext/__init__.py

Whitespace-only changes.

docs/_ext/playground.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# docs/_ext/playground.py
2+
3+
import logging
4+
from pathlib import Path
5+
from uuid import uuid4
6+
7+
from docutils import nodes
8+
from docutils.parsers.rst import Directive
9+
from jinja2 import Environment, FileSystemLoader
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
class WasmPlayground(Directive):
15+
"""
16+
A custom Sphinx directive to embed a Pyodide-powered code playground.
17+
"""
18+
19+
logger.info("Initializing WasmPlayground directive")
20+
has_content = True
21+
22+
def run(self):
23+
# Generate unique IDs for the HTML elements
24+
id = uuid4().hex
25+
print(id)
26+
env = Environment(loader=FileSystemLoader(Path(__file__).parent))
27+
template = env.get_template("playground_template.html")
28+
rendered = template.render(id=id)
29+
return [nodes.raw(text=rendered, format="html")]
30+
31+
32+
def setup(app):
33+
"""
34+
Register the directive with Sphinx.
35+
"""
36+
app.add_js_file("https://cdn.jsdelivr.net/pyodide/v0.29.0/full/pyodide.js", priority=100)
37+
app.add_directive("wasm-playground", WasmPlayground)
38+
return {
39+
"version": "1.0",
40+
"parallel_read_safe": True,
41+
"parallel_write_safe": True,
42+
}
43+

docs/_ext/playground_template.html

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<h2>🐍 Pyodide + SQLSpec Runner</h2>
2+
<textarea id="editor-{{ uuid }}">
3+
# Try Python code here
4+
import sqlspec
5+
print("sqlspec version:", sqlspec.__version__)
6+
</textarea>
7+
<br>
8+
<button id="run-button-{{ uuid }}">Loading...</button>
9+
10+
<pre id="output-{{ uuid }}"></pre>
11+
12+
<!-- ✅ Load Pyodide runtime -->
13+
<script src="https://cdn.jsdelivr.net/pyodide/v0.29.0/full/pyodide.js"></script>
14+
15+
<!-- ✅ Your app logic -->
16+
<script>
17+
(async function() {
18+
const editor = document.getElementById("editor-{{ uuid }}");
19+
const output = document.getElementById("output-{{ uuid }}");
20+
const runButton = document.getElementById("run-button-{{ uuid }}");
21+
22+
let pyodide = null;
23+
24+
function writeToOutput(s) {
25+
output.textContent += s + "\n";
26+
output.scrollTop = output.scrollHeight;
27+
}
28+
29+
async function initPyodide() {
30+
try {
31+
pyodide = await loadPyodide();
32+
pyodide.setStdout({ batched: writeToOutput });
33+
pyodide.setStderr({ batched: writeToOutput });
34+
output.textContent = "✅ Pyodide loaded.\nInstalling micropip...\n";
35+
36+
await pyodide.loadPackage("micropip");
37+
const micropip = pyodide.pyimport("micropip");
38+
39+
40+
output.textContent += "Installing sqlspec (this may take ~30s)...\n";
41+
await micropip.install("sqlspec[aiosqlite]");
42+
await micropip.install("sqlite3");
43+
44+
output.textContent += "✅ Ready! You can now run Python code.\n";
45+
runButton.disabled = false;
46+
runButton.textContent = "▶ Run";
47+
} catch (err) {
48+
output.textContent = "❌ Error loading Pyodide: " + err;
49+
console.error(err);
50+
}
51+
}
52+
53+
runButton.disabled = true;
54+
runButton.textContent = "Loading...";
55+
runButton.onclick = async () => {
56+
if (!pyodide) {
57+
output.textContent = "Pyodide is not loaded yet.";
58+
return;
59+
}
60+
const code = editor.value;
61+
output.textContent = "Running code...\n";
62+
try {
63+
await pyodide.runPythonAsync(code);
64+
} catch (err) {
65+
writeToOutput("Python Error: " + err);
66+
console.error(err);
67+
}
68+
};
69+
70+
initPyodide();
71+
})();
72+
</script>

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"sphinx_togglebutton",
6262
"sphinx_paramlinks",
6363
"sphinxcontrib.mermaid",
64+
"docs._ext.playground"
6465
]
6566
intersphinx_mapping = {
6667
"python": ("https://docs.python.org/3", None),

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ SQLSpec is **NOT an ORM**. It is a flexible connectivity layer that provides a c
5151
usage/index
5252
examples/index
5353
reference/index
54+
playground
5455

5556
.. toctree::
5657
:hidden:

docs/playground.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
======================
2+
Live Playground
3+
======================
4+
5+
You can try ``sqlspec`` live in your browser. The code block below
6+
is a full, interactive Python environment powered by Pyodide
7+
(WebAssembly).
8+
9+
.. wasm-playground::

0 commit comments

Comments
 (0)