mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-22 19:53:00 -05:00
support different pyodide versions (#328)
* add PyLoader class * create global loader during app creation time and remove it when pyscript loading operations are done * make the loader global and open/close when apps is starting. Also add concept of app config so users can set if they want to autoclose the loader of handle it themselves * add pyconfig file * auto add global config if there's no config set in the page * export initializer type * define type for config * move initialization out of svelte file, into app config * change runtimes from strings to objects * fix typo
This commit is contained in:
@@ -1,76 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Tailwind from './Tailwind.svelte';
|
||||
import { loadInterpreter } from './interpreter';
|
||||
import type { AppConfig } from './components/pyconfig';
|
||||
import { initializers, loadedEnvironments, mode, postInitializers, pyodideLoaded, scriptsQueue, globalLoader, appConfig } from './stores';
|
||||
|
||||
let pyodideReadyPromise;
|
||||
|
||||
let loader;
|
||||
let appConfig_: AppConfig = {
|
||||
autoclose_loader: true,
|
||||
};
|
||||
|
||||
globalLoader.subscribe(value => {
|
||||
loader = value;
|
||||
});
|
||||
|
||||
appConfig.subscribe( (value:AppConfig) => {
|
||||
if (value){
|
||||
appConfig_ = value;
|
||||
}
|
||||
console.log("config set!")
|
||||
});
|
||||
|
||||
const initializePyodide = async () => {
|
||||
loader.log("Loading runtime...")
|
||||
pyodideReadyPromise = loadInterpreter();
|
||||
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 (let 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 (let script of $scriptsQueue) {
|
||||
await 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(async () => {
|
||||
for (let initializer of $postInitializers) {
|
||||
await initializer();
|
||||
}
|
||||
}, 3000);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style global>
|
||||
@@ -105,8 +34,4 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<svelte:head>
|
||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
||||
</svelte:head>
|
||||
|
||||
<Tailwind />
|
||||
|
||||
@@ -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<Runtime>;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ import { getLastPath } from './utils';
|
||||
let pyodideReadyPromise;
|
||||
let pyodide;
|
||||
|
||||
const loadInterpreter = async function (): Promise<any> {
|
||||
const loadInterpreter = async function (indexUrl:string): Promise<any> {
|
||||
console.log('creating pyodide runtime');
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
pyodide = await loadPyodide({
|
||||
// indexURL: indexUrl,
|
||||
stdout: console.log,
|
||||
stderr: console.log,
|
||||
fullStdLib: false
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import type { PyScript } from './components/pyscript';
|
||||
|
||||
type Initializer = () => Promise<void>;
|
||||
export type Initializer = () => Promise<void>;
|
||||
|
||||
export const pyodideLoaded = writable({
|
||||
loaded: false,
|
||||
|
||||
Reference in New Issue
Block a user