mirror of
https://github.com/pyscript/pyscript.git
synced 2025-12-20 18:55:29 -05:00
Refactor py-config and the general initialization logic of the page (#806)
This PR is the first step to improve and rationalize the life-cycle of a pyscript app along the lines of what I described in #763 . It is not a complete solution, more PRs will follow. Highlights: - py-config is no longer a web component: the old code relied on PyConfig.connectedCallback to do some logic, but then if no <py-config> tag was present, we had to introduce a dummy one with the sole goal of activating the callback. Now the logic is much more linear. - the new pyconfig.ts only contains the code which is needed to parse the config; I also moved some relevant code from utils.ts because it didn't really belong to it - the old PyConfig class did much more than dealing with the config: in particular, it contained the code to initialize the env and the runtime. Now this logic has been moved directly into main.ts, inside the new PyScriptApp class. I plan to refactor the initialization code in further PRs - the current code relies too much on global state and global variables, they are everywhere. This PR is a first step to solve the problem by introducing a PyScriptApp class, which will hold all the mutable state of the page. Currently only config is stored there, but eventually I will migrate more state to it, until we will have only one global singleton, globalApp - thanks to what I described above, I could kill the appConfig svelte store: one less store to kill :).
This commit is contained in:
@@ -1,31 +1,104 @@
|
||||
import './styles/pyscript_base.css';
|
||||
|
||||
import { loadConfigFromElement } from './pyconfig';
|
||||
import type { AppConfig } from './pyconfig';
|
||||
import type { Runtime } from './runtime';
|
||||
import { PyScript } from './components/pyscript';
|
||||
import { PyEnv } from './components/pyenv';
|
||||
import { PyLoader } from './components/pyloader';
|
||||
import { PyConfig } from './components/pyconfig';
|
||||
import { PyodideRuntime } from './pyodide';
|
||||
import { getLogger } from './logger';
|
||||
import { globalLoader } from './stores';
|
||||
import { globalLoader, runtimeLoaded, addInitializer } from './stores';
|
||||
import { handleFetchError, globalExport } from './utils'
|
||||
|
||||
const logger = getLogger('pyscript/main');
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const xPyScript = customElements.define('py-script', PyScript);
|
||||
const xPyLoader = customElements.define('py-loader', PyLoader);
|
||||
const xPyConfig = customElements.define('py-config', PyConfig);
|
||||
const xPyEnv = customElements.define('py-env', PyEnv);
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// XXX this should be killed eventually
|
||||
let runtimeSpec: Runtime;
|
||||
runtimeLoaded.subscribe(value => {
|
||||
runtimeSpec = value;
|
||||
});
|
||||
|
||||
|
||||
class PyScriptApp {
|
||||
|
||||
config: AppConfig;
|
||||
|
||||
main() {
|
||||
this.loadConfig();
|
||||
this.initialize();
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const xPyScript = customElements.define('py-script', PyScript);
|
||||
const xPyLoader = customElements.define('py-loader', PyLoader);
|
||||
const xPyEnv = customElements.define('py-env', PyEnv);
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
// add loader to the page body
|
||||
logger.info('add py-loader');
|
||||
const loader = <PyLoader>document.createElement('py-loader');
|
||||
document.body.append(loader);
|
||||
globalLoader.set(loader);
|
||||
}
|
||||
|
||||
loadConfig() {
|
||||
// find the <py-config> tag. If not found, we get null which means
|
||||
// "use the default config"
|
||||
// XXX: we should actively complain if there are multiple <py-config>
|
||||
// and show a big error. PRs welcome :)
|
||||
logger.info('searching for <py-config>');
|
||||
const el = document.querySelector('py-config');
|
||||
this.config = loadConfigFromElement(el);
|
||||
logger.info('config loaded:\n' + JSON.stringify(this.config, null, 2));
|
||||
}
|
||||
|
||||
initialize() {
|
||||
addInitializer(this.loadPackages);
|
||||
addInitializer(this.loadPaths);
|
||||
this.loadRuntimes();
|
||||
}
|
||||
|
||||
loadPackages = async () => {
|
||||
logger.info("Packages to install: ", this.config.packages);
|
||||
await runtimeSpec.installPackage(this.config.packages);
|
||||
}
|
||||
|
||||
loadPaths = async () => {
|
||||
const paths = this.config.paths;
|
||||
logger.info("Paths to load: ", paths)
|
||||
for (const singleFile of paths) {
|
||||
logger.info(` loading path: ${singleFile}`);
|
||||
try {
|
||||
await runtimeSpec.loadFromFile(singleFile);
|
||||
} catch (e) {
|
||||
//Should we still export full error contents to console?
|
||||
handleFetchError(<Error>e, singleFile);
|
||||
}
|
||||
}
|
||||
logger.info("All paths loaded");
|
||||
}
|
||||
|
||||
loadRuntimes() {
|
||||
logger.info('Initializing runtimes');
|
||||
for (const runtime of this.config.runtimes) {
|
||||
const runtimeObj: Runtime = new PyodideRuntime(this.config, runtime.src,
|
||||
runtime.name, runtime.lang);
|
||||
const script = document.createElement('script'); // create a script DOM node
|
||||
script.src = runtimeObj.src; // set its src to the provided URL
|
||||
script.addEventListener('load', () => {
|
||||
void runtimeObj.initialize();
|
||||
});
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
// As first thing, loop for application configs
|
||||
logger.info('checking for py-config');
|
||||
const config: PyConfig = document.querySelector('py-config');
|
||||
if (!config) {
|
||||
const loader = document.createElement('py-config');
|
||||
document.body.append(loader);
|
||||
}
|
||||
|
||||
// add loader to the page body
|
||||
logger.info('add py-loader');
|
||||
const loader = <PyLoader>document.createElement('py-loader');
|
||||
document.body.append(loader);
|
||||
globalLoader.set(loader);
|
||||
function pyscript_get_config() {
|
||||
return globalApp.config;
|
||||
}
|
||||
globalExport('pyscript_get_config', pyscript_get_config);
|
||||
|
||||
// main entry point of execution
|
||||
const globalApp = new PyScriptApp();
|
||||
globalApp.main();
|
||||
|
||||
Reference in New Issue
Block a user