[next] Porting most basic examples (#1631)

This commit is contained in:
Andrea Giammarchi
2023-08-10 22:42:01 +02:00
committed by GitHub
parent 8a1db288fc
commit 75a57a49f5
38 changed files with 451 additions and 70 deletions

View File

@@ -8,7 +8,15 @@
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
@@ -33,6 +41,7 @@
]
</py-config>
<py-script>
from pyscript import display
import altair as alt
from vega_datasets import data

View File

@@ -32,8 +32,16 @@
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">

View File

@@ -7,8 +7,16 @@
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
<nav class="navbar" style="background-color: #000000">
@@ -34,6 +42,7 @@
</py-config>
<py-script>
from pyscript import display
import folium
import json
import pandas as pd

View File

@@ -12,7 +12,15 @@
href="https://pyscript.net/latest/pyscript.css"
/>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
@@ -36,6 +44,7 @@
Hello world! <br />
This is the current date and time, as computed by Python:
<py-script>
from pyscript import display
from datetime import datetime
now = datetime.now()
display(now.strftime("%m/%d/%Y, %H:%M:%S"))

View File

@@ -7,7 +7,10 @@
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
</head>
<body>

View File

@@ -28,7 +28,8 @@
<script defer src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js"></script>
<script defer src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@holoviz/panel@0.14.1/dist/panel.min.js"></script>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<style>py-script{display:none}</style>
<script type="module" src="https://esm.sh/@pyscript/core@latest/core.js"></script>
<py-config>
packages = [
"https://cdn.holoviz.org/panel/0.14.3/dist/wheels/bokeh-2.4.3-py3-none-any.whl",

View File

@@ -12,8 +12,16 @@
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
@@ -27,10 +35,10 @@
</nav>
<section class="pyscript">
<div class="font-mono">
start time: <label id="outputDiv"></label>
start time: <label id="output1"></label>
</div>
<div id="outputDiv2" class="font-mono"></div>
<div id="outputDiv3" class="font-mono"></div>
<div id="output2" class="font-mono"></div>
<div id="output3" class="font-mono"></div>
<py-tutor modules="utils.py">
<py-config>
@@ -40,12 +48,14 @@
[[fetch]]
files = ["./utils.py"]
</py-config>
<py-script>
<script type="py">
import utils
display(utils.now())
</py-script>
from pyscript import display
display(utils.now(), target="output1")
</script>
<py-script>
from pyscript import display
from utils import now
import asyncio
@@ -53,15 +63,14 @@
while True:
await asyncio.sleep(1)
output = now()
Element("outputDiv2").write(output)
display(output, target="output2")
out3 = Element("outputDiv3")
if output[-1] in ["0", "4", "8"]:
out3.write("It's espresso time!")
display("It's espresso time!", target="output3")
else:
out3.clear()
display("", target="output3")
pyscript.run_until_complete(foo())
foo()
</py-script>
</py-tutor>
</section>

View File

@@ -12,8 +12,11 @@
href="https://pyscript.net/latest/pyscript.css"
/>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link rel="stylesheet" href="./assets/css/examples.css" />
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
</head>
<body>
@@ -26,14 +29,14 @@
</div>
</nav>
<section class="pyscript">
<py-tutor modules="./utils.py;./todo.py">
<py-tutor modules="./todo.py">
<py-config>
plugins = [
"https://pyscript.net/latest/plugins/python/py_tutor.py",
"./py_list.py"
]
[[fetch]]
files = ["./utils.py", "./todo.py"]
files = ["./todo.py"]
</py-config>
<py-script src="./todo.py"></py-script>
@@ -57,7 +60,7 @@
id="new-task-btn"
class="py-button"
type="submit"
py-click="add_task()"
py-click="add_task"
>
Add task
</button>

View File

@@ -1,25 +1,30 @@
from datetime import datetime as dt
from utils import add_class, remove_class
from pyscript import document
tasks = []
def q(selector, root=document):
return root.querySelector(selector)
# define the task template that will be use to render new templates to the page
task_template = Element("task-template").select(".task", from_content=True)
task_list = Element("list-tasks-container")
new_task_content = Element("new-task-content")
task_template = q("#task-template").content.querySelector(".task")
task_list = q("#list-tasks-container")
new_task_content = q("#new-task-content")
def add_task(*args, **kws):
def add_task(e):
# ignore empty task
if not new_task_content.element.value:
if not new_task_content.value:
return None
# create task
task_id = f"task-{len(tasks)}"
task = {
"id": task_id,
"content": new_task_content.element.value,
"content": new_task_content.value,
"done": False,
"created_at": dt.now(),
}
@@ -28,26 +33,24 @@ def add_task(*args, **kws):
# add the task element to the page as new node in the list by cloning from a
# template
task_html = task_template.clone(task_id)
task_html_content = task_html.select("p")
task_html_content.element.innerText = task["content"]
task_html_check = task_html.select("input")
task_list.element.appendChild(task_html.element)
task_html = task_template.cloneNode(True)
task_html.id = task_id
task_html_check = q("input", root=task_html)
task_html_content = q("p", root=task_html)
task_html_content.textContent = task["content"]
task_list.append(task_html)
def check_task(evt=None):
task["done"] = not task["done"]
if task["done"]:
add_class(task_html_content, "line-through")
else:
remove_class(task_html_content, "line-through")
task_html_content.classList.toggle("line-through", task["done"])
new_task_content.clear()
task_html_check.element.onclick = check_task
new_task_content.value = ""
task_html_check.onclick = check_task
def add_task_event(e):
if e.key == "Enter":
add_task()
add_task(e)
new_task_content.element.onkeypress = add_task_event
new_task_content.onkeypress = add_task_event

View File

@@ -10,8 +10,8 @@ def now(fmt="%m/%d/%Y, %H:%M:%S"):
def remove_class(element, class_name):
element.element.classList.remove(class_name)
element.classList.remove(class_name)
def add_class(element, class_name):
element.element.classList.add(class_name)
element.classList.add(class_name)

View File

@@ -21,19 +21,26 @@
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/0.147.0/three.min.js"></script>
<script defer src="https://pyscript.net/latest/pyscript.js"></script>
<link
rel="stylesheet"
href="https://pyscript.net/latest/pyscript.css"
/>
<style>
py-script {
display: none;
}
</style>
<script
type="module"
src="https://esm.sh/@pyscript/core@latest/core.js"
></script>
<py-script>
from pyodide.ffi import create_proxy, to_js
from js import window
from pyscript import window, document
from js import Math
from js import THREE
from js import performance
from js import Object
from js import document
import asyncio
mouse = THREE.Vector2.new();

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,17 @@
{
"name": "@pyscript/core",
"version": "0.1.3",
"version": "0.1.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pyscript/core",
"version": "0.1.3",
"version": "0.1.4",
"license": "APACHE-2.0",
"dependencies": {
"@ungap/with-resolvers": "^0.1.0",
"basic-devtools": "^0.1.6",
"polyscript": "^0.1.9"
"polyscript": "^0.1.10"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.1.0",
@@ -935,9 +935,9 @@
}
},
"node_modules/polyscript": {
"version": "0.1.9",
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.1.9.tgz",
"integrity": "sha512-UhBcpN7FAE7MZPg10cQOXpIE9vLG6cYOrQRX37CPps4Rvl8vbUgHkzkB3qsd1DL5ztMykfeSvYPI+AIP7lx4Yw==",
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.1.10.tgz",
"integrity": "sha512-NdO0inJIJyNgM8bYvD9z8aYbs1chQWq3iNtXjj7YrDT84A7IOVELPCFDlXzLU8UngpO/yQm3j4YrEoln89LbsA==",
"dependencies": {
"@ungap/structured-clone": "^1.2.0",
"@ungap/with-resolvers": "^0.1.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@pyscript/core",
"version": "0.1.3",
"version": "0.1.4",
"type": "module",
"description": "PyScript",
"main": "core.js",
@@ -32,7 +32,7 @@
"dependencies": {
"@ungap/with-resolvers": "^0.1.0",
"basic-devtools": "^0.1.6",
"polyscript": "^0.1.9"
"polyscript": "^0.1.10"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.1.0",

View File

@@ -54,15 +54,12 @@ const after = () => {
* It either throws an error if the 'src' can't be fetched or it returns a fallback
* content as source.
*/
const fetchSource = async (tag) => {
const fetchSource = async (tag, io) => {
if (tag.hasAttribute("src")) {
try {
const response = await fetch(tag.getAttribute("src"));
return response.then(getText);
return await fetch(tag.getAttribute("src")).then(getText);
} catch (error) {
// TODO _createAlertBanner(err) instead ?
alert(error.message);
throw error;
io.stderr(error);
}
}
return tag.textContent;
@@ -105,8 +102,16 @@ const registerModule = ({ XWorker: $XWorker, interpreter, io }) => {
? currentElement.target.id
: currentElement.id;
return (what, target = id, append = true) => {
pyDisplay.callKwargs(...[].concat(what), { target, append });
return (...args) => {
const last = args.at(-1);
let kw = { target: id, append: false };
if (
typeof last === "object" &&
last &&
("target" in last || "append" in last)
)
kw = { ...kw, ...args.pop() };
pyDisplay.callKwargs(...args, kw);
};
},
});
@@ -199,7 +204,9 @@ define("py", {
// document.currentScript.target if needed
defineProperty(element, "target", { value: show });
pyodide[`run${isAsync ? "Async" : ""}`](await fetchSource(element));
pyodide[`run${isAsync ? "Async" : ""}`](
await fetchSource(element, pyodide.io),
);
} else {
// resolve PyScriptElement to allow connectedCallback
element._pyodide.resolve(pyodide);
@@ -217,11 +224,10 @@ class PyScriptElement extends HTMLElement {
async connectedCallback() {
if (!this.executed) {
this.executed = true;
const { run } = await this._pyodide.promise;
this.srcCode = await fetchSource(this);
const { io, run } = await this._pyodide.promise;
this.srcCode = await fetchSource(this, io);
this.textContent = "";
const result = run(this.srcCode);
if (!this.textContent && result) this.textContent = result;
run(this.srcCode);
this.style.display = "block";
}
}

View File

@@ -10,7 +10,7 @@
<body>
<script type="py">
from pyscript import display
display("Hello PyScript Next")
display("Hello", "PyScript Next", append=False)
</script>
</body>
</html>

View File

@@ -0,0 +1,10 @@
export { ie as default };
declare function ie(e: any, ...r: any[]): any;
declare namespace ie {
import transfer = m.transfer;
export { transfer };
}
declare function m(t: any, { parse: n, stringify: r, transform: u }?: JSON): any;
declare namespace m {
function transfer(...e: any[]): any[];
}

View File

@@ -0,0 +1,54 @@
export const CUSTOM_SELECTORS: any[];
export function handleCustomType(node: Element): void;
export function define(type: string, options: CustomOptions): void;
export function whenDefined(type: string): Promise<object>;
/**
* custom configuration
*/
export type Runtime = {
/**
* the bootstrapped interpreter
*/
interpreter: object;
/**
* an XWorker constructor that defaults to same interpreter on the Worker.
*/
XWorker: (url: string, options?: object) => Worker;
/**
* a cloned config used to bootstrap the interpreter
*/
config: object;
/**
* an utility to run code within the interpreter
*/
run: (code: string) => any;
/**
* an utility to run code asynchronously within the interpreter
*/
runAsync: (code: string) => Promise<any>;
/**
* an utility to write a file in the virtual FS, if available
*/
writeFile: (path: string, data: ArrayBuffer) => void;
};
/**
* custom configuration
*/
export type CustomOptions = {
/**
* the interpreter to use
*/
interpreter: 'pyodide' | 'micropython' | 'wasmoon' | 'ruby-wasm-wasi';
/**
* the optional interpreter version to use
*/
version?: string;
/**
* the optional config to use within such interpreter
*/
config?: string;
/**
* the callback that will be invoked once
*/
onInterpreterReady?: (environment: object, node: Element) => void;
};

View File

@@ -0,0 +1,3 @@
export function getBuffer(response: Response): Promise<ArrayBuffer>;
export function getJSON(response: Response): Promise<any>;
export function getText(response: Response): Promise<string>;

View File

@@ -0,0 +1,3 @@
export { env } from "./listeners.js";
export const XWorker: (url: string, options?: import("./worker/class.js").WorkerOptions) => Worker;
export { define, whenDefined } from "./custom.js";

View File

@@ -0,0 +1,4 @@
export function registerJSModule(interpreter: any, name: any, value: any): void;
export function run(interpreter: any, code: any): any;
export function runAsync(interpreter: any, code: any): any;
export function runEvent(interpreter: any, code: any, event: any): Promise<void>;

View File

@@ -0,0 +1,15 @@
export function clean(code: string): string;
export const io: WeakMap<object, any>;
export function stdio(init: any): {
stderr: (...args: any[]) => any;
stdout: (...args: any[]) => any;
get(engine: any): Promise<any>;
};
export function writeFile({ FS, PATH, PATH_FS }: {
FS: any;
PATH: any;
PATH_FS: any;
}, path: any, buffer: any): any;
export function writeFileShim(FS: any, path: any, buffer: any): any;
export const base: WeakMap<object, any>;
export function fetchPaths(module: any, interpreter: any, config_fetch: any): Promise<any[]>;

View File

@@ -0,0 +1,25 @@
declare namespace _default {
export { type };
export function module(version?: string): string;
export function engine({ loadMicroPython }: {
loadMicroPython: any;
}, config: any, url: any): Promise<any>;
export { registerJSModule };
export { run };
export { runAsync };
export { runEvent };
export function transform(_: any, value: any): any;
export function writeFile({ FS, _module: { PATH, PATH_FS } }: {
FS: any;
_module: {
PATH: any;
PATH_FS: any;
};
}, path: any, buffer: any): any;
}
export default _default;
declare const type: "micropython";
import { registerJSModule } from './_python.js';
import { run } from './_python.js';
import { runAsync } from './_python.js';
import { runEvent } from './_python.js';

View File

@@ -0,0 +1,25 @@
declare namespace _default {
export { type };
export function module(version?: string): string;
export function engine({ loadPyodide }: {
loadPyodide: any;
}, config: any, url: any): Promise<any>;
export { registerJSModule };
export { run };
export { runAsync };
export { runEvent };
export function transform(interpreter: any, value: any): any;
export function writeFile({ FS, PATH, _module: { PATH_FS } }: {
FS: any;
PATH: any;
_module: {
PATH_FS: any;
};
}, path: any, buffer: any): any;
}
export default _default;
declare const type: "pyodide";
import { registerJSModule } from './_python.js';
import { run } from './_python.js';
import { runAsync } from './_python.js';
import { runEvent } from './_python.js';

View File

@@ -0,0 +1,16 @@
declare namespace _default {
export { type };
export let experimental: boolean;
export function module(version?: string): string;
export function engine({ DefaultRubyVM }: {
DefaultRubyVM: any;
}, config: any, url: any): Promise<any>;
export function registerJSModule(interpreter: any, _: any, value: any): void;
export function run(interpreter: any, code: any): any;
export function runAsync(interpreter: any, code: any): any;
export function runEvent(interpreter: any, code: any, event: any): Promise<void>;
export function transform(_: any, value: any): any;
export function writeFile(): never;
}
export default _default;
declare const type: "ruby-wasm-wasi";

View File

@@ -0,0 +1,22 @@
declare namespace _default {
export { type };
export function module(version?: string): string;
export function engine({ LuaFactory, LuaLibraries }: {
LuaFactory: any;
LuaLibraries: any;
}, config: any): Promise<any>;
export function registerJSModule(interpreter: any, _: any, value: any): void;
export function run(interpreter: any, code: any): any;
export function runAsync(interpreter: any, code: any): any;
export function runEvent(interpreter: any, code: any, event: any): Promise<void>;
export function transform(_: any, value: any): any;
export function writeFile({ cmodule: { module: { FS }, }, }: {
cmodule: {
module: {
FS: any;
};
};
}, path: any, buffer: any): any;
}
export default _default;
declare const type: "wasmoon";

View File

@@ -0,0 +1,9 @@
/** @type {Map<string, object>} */
export const registry: Map<string, object>;
/** @type {Map<string, object>} */
export const configs: Map<string, object>;
/** @type {string[]} */
export const selectors: string[];
/** @type {string[]} */
export const prefixes: string[];
export const interpreter: Map<any, any>;

View File

@@ -0,0 +1,3 @@
export const env: any;
export function listener(event: any): Promise<void>;
export function addAllListeners(root: Document | Element): void;

View File

@@ -0,0 +1,2 @@
export function getRuntime(id: string, config?: string, options?: object): Promise<any>;
export function getRuntimeID(type: string, version?: string): string;

View File

@@ -0,0 +1,4 @@
export function queryTarget(script: any, idOrSelector: any): any;
export const interpreters: Map<any, any>;
export function getDetails(type: any, id: any, name: any, version: any, config: any, runtime?: any): any;
export function handle(script: HTMLScriptElement): Promise<void>;

View File

@@ -0,0 +1 @@
export function parse(text: string): object;

View File

@@ -0,0 +1,29 @@
export const isArray: (arg: any) => arg is any[];
export const assign: {
<T extends {}, U>(target: T, source: U): T & U;
<T_1 extends {}, U_1, V>(target: T_1, source1: U_1, source2: V): T_1 & U_1 & V;
<T_2 extends {}, U_2, V_1, W>(target: T_2, source1: U_2, source2: V_1, source3: W): T_2 & U_2 & V_1 & W;
(target: object, ...sources: any[]): any;
};
export const create: {
(o: object): any;
(o: object, properties: PropertyDescriptorMap & ThisType<any>): any;
};
export const defineProperties: <T>(o: T, properties: PropertyDescriptorMap & ThisType<any>) => T;
export const defineProperty: <T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>) => T;
export const entries: {
<T>(o: {
[s: string]: T;
} | ArrayLike<T>): [string, T][];
(o: {}): [string, any][];
};
export const all: {
<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>[]>;
<T_1 extends [] | readonly unknown[]>(values: T_1): Promise<{ -readonly [P in keyof T_1]: Awaited<T_1[P]>; }>;
};
export const resolve: {
(): Promise<void>;
<T>(value: T): Promise<Awaited<T>>;
<T_1>(value: T_1 | PromiseLike<T_1>): Promise<Awaited<T_1>>;
};
export function absoluteURL(path: any, base?: string): string;

View File

@@ -0,0 +1,19 @@
declare function _default(...args: any[]): (url: string, options?: WorkerOptions) => Worker;
export default _default;
/**
* custom configuration
*/
export type WorkerOptions = {
/**
* the interpreter type to use
*/
type: string;
/**
* the optional interpreter version to use
*/
version?: string;
/**
* the optional config to use within such interpreter
*/
config?: string;
};

View File

@@ -0,0 +1,6 @@
export class Hook {
constructor(interpreter: any, options: any);
interpreter: any;
onWorkerReady: any;
get stringHooks(): {};
}

View File

@@ -0,0 +1,2 @@
declare function _default(): Worker;
export default _default;

View File

@@ -0,0 +1,25 @@
/**
* A `Worker` facade able to bootstrap on the worker thread only a PyScript module.
* @param {string} file the python file to run ina worker.
* @param {{config?: string | object, async?: boolean}} [options] optional configuration for the worker.
* @returns {Worker & {sync: ProxyHandler<object>}}
*/
export function PyWorker(file: string, options?: {
config?: string | object;
async?: boolean;
}): Worker & {
sync: ProxyHandler<object>;
};
export namespace hooks {
let onBeforeRun: Set<Function>;
let onBeforeRunAync: Set<Function>;
let onAfterRun: Set<Function>;
let onAfterRunAsync: Set<Function>;
let onInterpreterReady: Set<Function>;
let codeBeforeRunWorker: Set<string>;
let codeBeforeRunWorkerAsync: Set<string>;
let codeAfterRunWorker: Set<string>;
let codeAfterRunWorkerAsync: Set<string>;
}
declare let config: any;
export {};

View File

@@ -0,0 +1,27 @@
export function _createAlertBanner(message: any, level: any, messageType?: string, logMessage?: boolean): void;
export namespace ErrorCode {
let GENERIC: string;
let FETCH_ERROR: string;
let FETCH_NAME_ERROR: string;
let FETCH_UNAUTHORIZED_ERROR: string;
let FETCH_FORBIDDEN_ERROR: string;
let FETCH_NOT_FOUND_ERROR: string;
let FETCH_SERVER_ERROR: string;
let FETCH_UNAVAILABLE_ERROR: string;
let BAD_CONFIG: string;
let MICROPIP_INSTALL_ERROR: string;
let BAD_PLUGIN_FILE_EXTENSION: string;
let NO_DEFAULT_EXPORT: string;
let TOP_LEVEL_AWAIT: string;
}
export class UserError extends Error {
constructor(errorCode: any, message?: string, messageType?: string);
errorCode: any;
messageType: string;
}
export class FetchError extends UserError {
constructor(errorCode: any, message: any);
}
export class InstallError extends UserError {
constructor(errorCode: any, message: any);
}

View File

@@ -0,0 +1,10 @@
/**
* This is a fetch wrapper that handles any non 200 responses and throws a
* FetchError with the right ErrorCode. This is useful because our FetchError
* will automatically create an alert banner.
*
* @param {string} url - URL to fetch
* @param {Request} [options] - options to pass to fetch
* @returns {Promise<Response>}
*/
export function robustFetch(url: string, options?: Request): Promise<Response>;