diff --git a/pyscriptjs/examples/simple_clock.html b/pyscriptjs/examples/simple_clock.html index 4747e8893e4..fa3f937d325 100644 --- a/pyscriptjs/examples/simple_clock.html +++ b/pyscriptjs/examples/simple_clock.html @@ -14,6 +14,14 @@ - paths: - ./utils.py + + - autoclose_loader: false + - runtimes: + - + src: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" + name: pyodide-0.20 + lang: python + @@ -43,6 +51,8 @@ else: out3.clear() +# close the global PyScript pyscript_loader +pyscript_loader.close() pyscript.run_until_complete(foo()) diff --git a/pyscriptjs/src/App.svelte b/pyscriptjs/src/App.svelte index 71c131978ca..b257500ec1c 100644 --- a/pyscriptjs/src/App.svelte +++ b/pyscriptjs/src/App.svelte @@ -1,76 +1,5 @@ - - - - diff --git a/pyscriptjs/src/components/pyconfig.ts b/pyscriptjs/src/components/pyconfig.ts index 6b928d89152..f6657ed5015 100644 --- a/pyscriptjs/src/components/pyconfig.ts +++ b/pyscriptjs/src/components/pyconfig.ts @@ -1,18 +1,133 @@ import * as jsyaml from 'js-yaml'; import { BaseEvalElement } from './base'; -import { appConfig } from '../stores'; +import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue, globalLoader, appConfig, Initializer } from '../stores'; +import { loadInterpreter } from '../interpreter'; +import type { PyScript } from './pyscript'; -let appConfig_; -appConfig.subscribe(value => { - appConfig_ = value; -}); +const DEFAULT_RUNTIME = { + src: "https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js", + name: "pyodide-default", + lang: "python" +} + +export type Runtime = { + src: string; + name?: string; + lang?: string; +}; export type AppConfig = { autoclose_loader: boolean; name?: string; version?: string; - }; + runtimes?: Array; +}; + +let appConfig_: AppConfig = { + autoclose_loader: true, +}; + +appConfig.subscribe( (value:AppConfig) => { + if (value){ + appConfig_ = value; + } + console.log("config set!") +}); + +let initializers_: Initializer[]; +initializers.subscribe( (value:Initializer[]) => { + initializers_ = value; + console.log("initializers set") +}); + +let postInitializers_: Initializer[]; +postInitializers.subscribe( (value:Initializer[]) => { + postInitializers_ = value; + console.log("post initializers set") +}); + +let scriptsQueue_: PyScript[]; +scriptsQueue.subscribe( (value: PyScript[]) => { + scriptsQueue_ = value; + console.log("post initializers set") +}); + +let mode_: string; +mode.subscribe( (value:string) => { + mode_ = value; + console.log("post initializers set") +}); + + +let pyodideReadyPromise; +let loader; + + +globalLoader.subscribe(value => { + loader = value; +}); + + +export class PyodideRuntime extends Object{ + src: string; + + constructor(url:string) { + super(); + this.src = url; + } + + async initialize(){ + loader.log("Loading runtime...") + pyodideReadyPromise = loadInterpreter(this.src); + const pyodide = await pyodideReadyPromise; + const newEnv = { + id: 'a', + promise: pyodideReadyPromise, + runtime: pyodide, + state: 'loading', + }; + pyodideLoaded.set(pyodide); + + // Inject the loader into the runtime namespace + pyodide.globals.set("pyscript_loader", loader); + + loader.log("Runtime created...") + loadedEnvironments.update((value: any): any => { + value[newEnv['id']] = newEnv; + }); + + // now we call all initializers before we actually executed all page scripts + loader.log("Initializing components...") + for (const initializer of initializers_) { + await initializer(); + } + + // now we can actually execute the page scripts if we are in play mode + loader.log("Initializing scripts...") + if (mode_ == 'play') { + for (const script of scriptsQueue_) { + script.evaluate(); + } + scriptsQueue.set([]); + } + + // now we call all post initializers AFTER we actually executed all page scripts + loader.log("Running post initializers..."); + + if (appConfig_ && appConfig_.autoclose_loader) { + loader.close(); + console.log("------ loader closed ------"); + } + + setTimeout(() => { + for (const initializer of postInitializers_) { + initializer(); + } + }, 3000); + } +} + export class PyConfig extends BaseEvalElement { shadow: ShadowRoot; @@ -33,14 +148,21 @@ export class PyConfig extends BaseEvalElement { this.code = this.innerHTML; this.innerHTML = ''; - this.values = jsyaml.load(this.code); - if (this.values === undefined){ + const loadedValues = jsyaml.load(this.code); + if (loadedValues === undefined){ this.values = { autoclose_loader: true, }; + }else{ + this.values = Object.assign({}, ...loadedValues); + } + if (this.values.runtimes === undefined){ + this.values.runtimes = [DEFAULT_RUNTIME]; } appConfig.set(this.values); console.log("config set", this.values); + + this.loadRuntimes(); } log(msg: string){ @@ -52,4 +174,17 @@ export class PyConfig extends BaseEvalElement { close() { this.remove(); } + + loadRuntimes(){ + console.log("Initializing runtimes...") + for (const runtime of this.values.runtimes) { + const script = document.createElement("script"); // create a script DOM node + const runtimeSpec = new PyodideRuntime(runtime.src); + script.src = runtime.src; // set its src to the provided URL + script.onload = () => { + runtimeSpec.initialize(); + } + document.head.appendChild(script); + } + } } diff --git a/pyscriptjs/src/interpreter.ts b/pyscriptjs/src/interpreter.ts index 918625f570a..22f5b78567b 100644 --- a/pyscriptjs/src/interpreter.ts +++ b/pyscriptjs/src/interpreter.ts @@ -3,11 +3,12 @@ import { getLastPath } from './utils'; let pyodideReadyPromise; let pyodide; -const loadInterpreter = async function (): Promise { +const loadInterpreter = async function (indexUrl:string): Promise { console.log('creating pyodide runtime'); // eslint-disable-next-line // @ts-ignore pyodide = await loadPyodide({ + // indexURL: indexUrl, stdout: console.log, stderr: console.log, fullStdLib: false diff --git a/pyscriptjs/src/main.ts b/pyscriptjs/src/main.ts index d04778b6ffa..21c6c9a82e0 100644 --- a/pyscriptjs/src/main.ts +++ b/pyscriptjs/src/main.ts @@ -23,9 +23,8 @@ const xPyWidget = customElements.define('py-register-widget', PyWidget); const xPyLoader = customElements.define('py-loader', PyLoader); const xPyConfig = customElements.define('py-config', PyConfig); - // As first thing, loop for application configs -const config = document.querySelector('py-config'); +const config: PyConfig = document.querySelector('py-config'); if (!config){ const loader = document.createElement('py-config'); document.body.append(loader); diff --git a/pyscriptjs/src/stores.ts b/pyscriptjs/src/stores.ts index b3c6cfbc341..065d2e96da4 100644 --- a/pyscriptjs/src/stores.ts +++ b/pyscriptjs/src/stores.ts @@ -1,7 +1,7 @@ import { writable } from 'svelte/store'; import type { PyScript } from './components/pyscript'; -type Initializer = () => Promise; +export type Initializer = () => Promise; export const pyodideLoaded = writable({ loaded: false, diff --git a/pyscriptjs/tsconfig.json b/pyscriptjs/tsconfig.json index 31509cef1a9..5b80dad3746 100644 --- a/pyscriptjs/tsconfig.json +++ b/pyscriptjs/tsconfig.json @@ -22,7 +22,6 @@ "sourceMap": true, /** Requests the runtime types from the svelte modules by default. Needed for TS files or else you get errors. */ "types": ["svelte"], - "strict": false, "esModuleInterop": true, "skipLibCheck": true,