-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: new Try-It page based on plain Pyodide (#3058)
* docs: new Try-It page based on plain Pyodide * fix link; no idea how to make it open in a new tab * make the 'try-it' button open a new tab * why wasn't it found? * was it the Unicode? * no, it was me not shift-reloading the page (to clear cache) * the arrow also has to be in the badge, not just alt-text
- Loading branch information
Showing
4 changed files
with
318 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
document.addEventListener("DOMContentLoaded", function() { | ||
document.querySelectorAll('a[href="_static/try-it.html"]').forEach(a => { | ||
a.target = "_blank"; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,307 @@ | ||
<!doctype html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<script src="https://cdn.jsdelivr.net/npm/jquery"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/js/jquery.terminal.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/js/unix_formatting.min.js"></script> | ||
<link | ||
href="https://cdn.jsdelivr.net/npm/[email protected]/css/jquery.terminal.min.css" | ||
rel="stylesheet" | ||
/> | ||
<style> | ||
.terminal { | ||
--size: 1.5; | ||
--color: rgba(255, 255, 255, 0.8); | ||
} | ||
.noblink { | ||
--animation: terminal-none; | ||
} | ||
body { | ||
background-color: black; | ||
} | ||
#jquery-terminal-logo { | ||
color: white; | ||
border-color: white; | ||
position: absolute; | ||
top: 7px; | ||
right: 18px; | ||
z-index: 2; | ||
} | ||
#jquery-terminal-logo a { | ||
color: gray; | ||
text-decoration: none; | ||
font-size: 0.7em; | ||
} | ||
#loading { | ||
display: inline-block; | ||
width: 50px; | ||
height: 50px; | ||
position: fixed; | ||
top: 50%; | ||
left: 50%; | ||
border: 3px solid rgba(172, 237, 255, 0.5); | ||
border-radius: 50%; | ||
border-top-color: #fff; | ||
animation: spin 1s ease-in-out infinite; | ||
-webkit-animation: spin 1s ease-in-out infinite; | ||
} | ||
|
||
@keyframes spin { | ||
to { | ||
-webkit-transform: rotate(360deg); | ||
} | ||
} | ||
@-webkit-keyframes spin { | ||
to { | ||
-webkit-transform: rotate(360deg); | ||
} | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="jquery-terminal-logo"> | ||
<a href="https://terminal.jcubic.pl/">jQuery Terminal</a> | ||
</div> | ||
<div id="loading"></div> | ||
<script> | ||
"use strict"; | ||
|
||
function sleep(s) { | ||
return new Promise((resolve) => setTimeout(resolve, s)); | ||
} | ||
|
||
async function main() { | ||
let indexURL = "https://cdn.jsdelivr.net/pyodide/v0.25.0/full/"; | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const buildParam = urlParams.get("build"); | ||
if (buildParam) { | ||
if (["full", "debug", "pyc"].includes(buildParam)) { | ||
indexURL = indexURL.replace( | ||
"/full/", | ||
"/" + urlParams.get("build") + "/", | ||
); | ||
} else { | ||
console.warn( | ||
'Invalid URL parameter: build="' + | ||
buildParam + | ||
'". Using default "full".', | ||
); | ||
} | ||
} | ||
const { loadPyodide } = await import(indexURL + "pyodide.mjs"); | ||
// to facilitate debugging | ||
globalThis.loadPyodide = loadPyodide; | ||
|
||
let term; | ||
globalThis.pyodide = await loadPyodide({ | ||
stdin: () => { | ||
let result = prompt(); | ||
echo(result); | ||
return result; | ||
}, | ||
}); | ||
let namespace = pyodide.globals.get("dict")(); | ||
pyodide.runPython( | ||
` | ||
import sys | ||
from pyodide.ffi import to_js | ||
from pyodide.console import PyodideConsole, repr_shorten, BANNER | ||
import __main__ | ||
BANNER = "Welcome to the Pyodide terminal emulator 🐍\\n" + BANNER | ||
pyconsole = PyodideConsole(__main__.__dict__) | ||
import builtins | ||
async def await_fut(fut): | ||
res = await fut | ||
if res is not None: | ||
builtins._ = res | ||
return to_js([res], depth=1) | ||
def clear_console(): | ||
pyconsole.buffer = [] | ||
`, | ||
{ globals: namespace }, | ||
); | ||
let repr_shorten = namespace.get("repr_shorten"); | ||
let banner = namespace.get("BANNER"); | ||
let await_fut = namespace.get("await_fut"); | ||
let pyconsole = namespace.get("pyconsole"); | ||
let clear_console = namespace.get("clear_console"); | ||
const echo = (msg, ...opts) => | ||
term.echo( | ||
msg | ||
.replaceAll("]]", "]]") | ||
.replaceAll("[[", "[["), | ||
...opts, | ||
); | ||
namespace.destroy(); | ||
|
||
let ps1 = ">>> ", | ||
ps2 = "... "; | ||
|
||
async function lock() { | ||
let resolve; | ||
let ready = term.ready; | ||
term.ready = new Promise((res) => (resolve = res)); | ||
await ready; | ||
return resolve; | ||
} | ||
|
||
async function interpreter(command) { | ||
let unlock = await lock(); | ||
term.pause(); | ||
// multiline should be split (useful when pasting) | ||
for (const c of command.split("\n")) { | ||
const escaped = c.replaceAll(/\u00a0/g, " "); | ||
let fut = pyconsole.push(escaped); | ||
term.set_prompt(fut.syntax_check === "incomplete" ? ps2 : ps1); | ||
switch (fut.syntax_check) { | ||
case "syntax-error": | ||
term.error(fut.formatted_error.trimEnd()); | ||
continue; | ||
case "incomplete": | ||
continue; | ||
case "complete": | ||
break; | ||
default: | ||
throw new Error(`Unexpected type ${ty}`); | ||
} | ||
// In JavaScript, await automatically also awaits any results of | ||
// awaits, so if an async function returns a future, it will await | ||
// the inner future too. This is not what we want so we | ||
// temporarily put it into a list to protect it. | ||
let wrapped = await_fut(fut); | ||
// complete case, get result / error and print it. | ||
try { | ||
let [value] = await wrapped; | ||
if (value !== undefined) { | ||
echo( | ||
repr_shorten.callKwargs(value, { | ||
separator: "\n<long output truncated>\n", | ||
}), | ||
); | ||
} | ||
if (value instanceof pyodide.ffi.PyProxy) { | ||
value.destroy(); | ||
} | ||
} catch (e) { | ||
if (e.constructor.name === "PythonError") { | ||
const message = fut.formatted_error || e.message; | ||
term.error(message.trimEnd()); | ||
} else { | ||
throw e; | ||
} | ||
} finally { | ||
fut.destroy(); | ||
wrapped.destroy(); | ||
} | ||
} | ||
term.resume(); | ||
await sleep(10); | ||
unlock(); | ||
} | ||
|
||
term = $("body").terminal(interpreter, { | ||
greetings: banner, | ||
prompt: ps1, | ||
completionEscape: false, | ||
completion: function (command, callback) { | ||
callback(pyconsole.complete(command).toJs()[0]); | ||
}, | ||
keymap: { | ||
"CTRL+C": async function (event, original) { | ||
clear_console(); | ||
term.enter(); | ||
echo("KeyboardInterrupt"); | ||
term.set_command(""); | ||
term.set_prompt(ps1); | ||
}, | ||
TAB: (event, original) => { | ||
const command = term.before_cursor(); | ||
// Disable completion for whitespaces. | ||
if (command.trim() === "") { | ||
term.insert("\t"); | ||
return false; | ||
} | ||
return original(event); | ||
}, | ||
}, | ||
}); | ||
window.term = term; | ||
pyconsole.stdout_callback = (s) => echo(s, { newline: false }); | ||
pyconsole.stderr_callback = (s) => { | ||
term.error(s.trimEnd()); | ||
}; | ||
term.ready = Promise.resolve(); | ||
pyodide._api.on_fatal = async (e) => { | ||
if (e.name === "Exit") { | ||
term.error(e); | ||
term.error("Pyodide exited and can no longer be used."); | ||
} else { | ||
term.error( | ||
"Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers.", | ||
); | ||
term.error("The cause of the fatal error was:"); | ||
term.error(e); | ||
term.error("Look in the browser console for more details."); | ||
} | ||
await term.ready; | ||
term.pause(); | ||
await sleep(15); | ||
term.pause(); | ||
}; | ||
|
||
const searchParams = new URLSearchParams(window.location.search); | ||
if (searchParams.has("noblink")) { | ||
$(".cmd-cursor").addClass("noblink"); | ||
} | ||
} | ||
window.console_ready = main(); | ||
</script> | ||
|
||
<script> | ||
"use strict"; | ||
|
||
window.console_ready.then(function() { | ||
term.echo("Loading NumPy and Awkward Array..."); | ||
document.getElementById("loading").style.zIndex = 1000; | ||
pyodide.loadPackage("micropip").then(function() { | ||
let namespace = pyodide.globals.get("dict")(); | ||
pyodide.runPython( | ||
` | ||
import micropip | ||
import asyncio | ||
loop = asyncio.get_event_loop() | ||
loop.run_until_complete(micropip.install("awkward==2.5.0")) | ||
`, | ||
{ globals: namespace }, | ||
).then(function() { | ||
namespace.destroy(); | ||
var command = ``; | ||
pyodide.runPython( | ||
` | ||
import numpy as np | ||
import awkward as ak | ||
example = ak.Array([ | ||
[{"x": 1.1, "y": [1]}, {"x": 2.2, "y": [1, 2]}, {"x": 3.3, "y": [1, 2, 3]}], | ||
[], | ||
[{"x": 4.4, "y": [1, 2, 3, 4]}, {"x": 5.5, "y": [1, 2, 3, 4, 5]}] | ||
]) | ||
`, | ||
); | ||
term.echo( | ||
`>>> import numpy as np | ||
>>> import awkward as ak | ||
>>> example = ak.Array([ | ||
... [{"x": 1.1, "y": [1]}, {"x": 2.2, "y": [1, 2]}, {"x": 3.3, "y": [1, 2, 3]}], | ||
... [], | ||
... [{"x": 4.4, "y": [1, 2, 3, 4]}, {"x": 5.5, "y": [1, 2, 3, 4, 5]}] | ||
... ])`, | ||
); | ||
document.getElementById("loading").style.zIndex = -1; | ||
}); | ||
}); | ||
}); | ||
</script> | ||
|
||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters