PyodideRuntime should be one of the runtimes (#698)

* PyodideRuntime should be one of the runtimes

* subsume interpreter into runtime API

* fix eslint

* add comments

* move initializers, postInitializers, scriptsQueue, etc. to initialize() of Runtime Super Class

* modify comment for initialize

* small renaming

* change id to default

* fix pyscript.py import

* try adding tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Add inlineDynamicImports option

* Make jest happy about ESM modules

* Attempt to make jest happy about pyodide

* point to version in accordance with node module being used

* fix base.ts

* fix tests

* fix indexURL path determination

* edit pyodide.asm.js as a part of setup process

* load runtime beforeAll tests

* add test for loading a package

* use only runPythonAsync underneath for pyodide

* import PyodideInterface type directly from pyodide

* add some comments

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Philipp Rudiger <prudiger@anaconda.com>
This commit is contained in:
Madhur Tandon
2022-08-25 02:33:36 +05:30
committed by GitHub
parent 1054e8e644
commit 1db155570d
17 changed files with 438 additions and 270 deletions

175
pyscriptjs/src/runtime.ts Normal file
View File

@@ -0,0 +1,175 @@
import type { PyodideInterface } from 'pyodide';
import type { PyLoader } from './components/pyloader';
import {
runtimeLoaded,
loadedEnvironments,
globalLoader,
initializers,
postInitializers,
Initializer,
scriptsQueue,
appConfig
} from './stores'
import type { PyScript } from './components/pyscript';
export type RuntimeInterpreter = PyodideInterface | null;
export type AppConfig = {
autoclose_loader: boolean;
name?: string;
version?: string;
runtimes?: Array<Runtime>;
};
let loader: PyLoader | undefined;
globalLoader.subscribe(value => {
loader = value;
});
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('scripts queue set');
});
let appConfig_: AppConfig = {
autoclose_loader: true,
};
appConfig.subscribe((value: AppConfig) => {
if (value) {
appConfig_ = value;
}
console.log('config set!');
});
/*
Runtime class is a super class that all different runtimes must respect
and adhere to.
Currently, the only runtime available is Pyodide as indicated by the
`RuntimeInterpreter` type above. This serves as a Union of types of
different runtimes/interpreters which will be added in near future.
The class has abstract methods available which each runtime is supposed
to implement.
Methods available handle loading of the interpreter, initialization,
running code, loading and installation of packages, loading from files etc.
For an example implementation, refer to the `PyodideRuntime` class
in `pyodide.ts`
*/
export abstract class Runtime extends Object {
abstract src: string;
abstract name?: string;
abstract lang?: string;
abstract interpreter: RuntimeInterpreter;
/**
* global symbols table for the underlying interpreter.
* */
abstract globals: any;
/**
* loads the interpreter for the runtime and saves an instance of it
* in the `this.interpreter` property along with calling of other
* additional convenience functions.
* */
abstract loadInterpreter(): Promise<void>;
/**
* delegates the code to be run to the underlying interpreter
* (asynchronously) which can call its own API behind the scenes.
* */
abstract run(code: string): Promise<any>;
/**
* delegates the setting of JS objects to
* the underlying interpreter.
* */
abstract registerJsModule(name: string, module: object): void;
/**
* delegates the loading of packages to
* the underlying interpreter.
* */
abstract loadPackage(names: string | string[]): Promise<void>;
/**
* delegates the installation of packages
* (using a package manager, which can be specific to
* the runtime) to the underlying interpreter.
*
* For Pyodide, we use `micropip`
* */
abstract installPackage(package_name: string | string[]): Promise<void>;
/**
* delegates the loading of files to the
* underlying interpreter.
* */
abstract loadFromFile(path: string): Promise<void>;
/**
* initializes the page which involves loading of runtime,
* as well as evaluating all the code inside <py-script> tags
* along with initializers and postInitializers
* */
async initialize(): Promise<void> {
loader?.log('Loading runtime...');
await this.loadInterpreter();
const newEnv = {
id: 'default',
runtime: this,
state: 'loading',
};
runtimeLoaded.set(this);
// Inject the loader into the runtime namespace
// eslint-disable-next-line
this.globals.set('pyscript_loader', loader);
loader?.log('Runtime created...');
loadedEnvironments.update(environments => ({
...environments,
[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();
}
loader?.log('Initializing scripts...');
for (const 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 ------');
}
for (const initializer of postInitializers_) {
await initializer();
}
console.log('===PyScript page fully initialized===');
}
}