8000 Refactor py-config and the general initialization logic of the page (… · pyscript/pyscript@c75f885 · GitHub
[go: up one dir, main page]

Skip to content

Commit c75f885

Browse files
authored
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 :).
1 parent 4011a51 commit c75f885

File tree

12 files changed

+526
-463
lines changed

12 files changed

+526
-463
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ repos:
6666
- --py310-plus
6767

6868
- repo: https://github.com/pre-commit/mirrors-eslint
69-
rev: v8.24.0
69+
rev: v8.23.1
7070
hooks:
7171
- id: eslint
7272
files: pyscriptjs/src/.*\.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx

pyscriptjs/src/components/pyconfig.ts

Lines changed: 0 additions & 130 deletions
This file was deleted.

pyscriptjs/src/main.ts

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,104 @@
11
import './styles/pyscript_base.css';
22

3+
import { loadConfigFromElement } from './pyconfig';
4+
import type { AppConfig } from './pyconfig';
5+
import type { Runtime } from './runtime';
36
import { PyScript } from './components/pyscript';
47
import { PyEnv } from './components/pyenv';
58
import { PyLoader } from './components/pyloader';
6-
import { PyConfig } from './components/pyconfig';
9+
import { PyodideRuntime } from './pyodide';
710
import { getLogger } from './logger';
8-
import { globalLoader } from './stores';
11+
import { globalLoader, runtimeLoaded, addInitializer } from './stores';
12+
import { handleFetchError, globalExport } from './utils'
913

1014
const logger = getLogger('pyscript/main');
1115

12-
/* eslint-disable @typescript-eslint/no-unused-vars */
13-
const xPyScript = customElements.define('py-script', PyScript);
14-
const xPyLoader = customElements.define('py-loader', PyLoader);
15-
const xPyConfig = customElements.define('py-config', PyConfig);
16-
const xPyEnv = customElements.define('py-env', PyEnv);
17-
/* eslint-disable @typescript-eslint/no-unused-vars */
18-
19-
// As first thing, loop for application configs
20-
logger.info('checking for py-config');
21-
const config: PyConfig = document.querySelector('py-config');
22-
if (!config) {
23-
const loader = document.createElement('py-config');
24-
document.body.append(loader);
16+
// XXX this should be killed eventually
17+
let runtimeSpec: Runtime;
18+
runtimeLoaded.subscribe(value => {
19+
runtimeSpec = value;
20+
});
21+
22+
23+
class PyScriptApp {
24+
25+
config: AppConfig;
26+
27+
main() {
28+
this.loadConfig();
29+
this.initialize();
30+
31+
/* eslint-disable @typescript-eslint/no-unused-vars */
32+
const xPyScript = customElements.define('py-script', PyScript);
33+
const xPyLoader = customElements.define('py-loader', PyLoader);
34+
const xPyEnv = customElements.define('py-env', PyEnv);
35+
/* eslint-disable @typescript-eslint/no-unused-vars */
36+
37+
// add loader to the page body
38+
logger.info('add py-loader');
39+
const loader = <PyLoader>document.createElement('py-loader');
40+
document.body.append(loader);
41+
globalLoader.set(loader);
42+
}
43+
44+
loadConfig() {
45+
// find the <py-config> tag. If not found, we get null which means
46+
// "use the default config"
47+
// XXX: we should actively complain if there are multiple <py-config>
48+
// and show a big error. PRs welcome :)
49+
logger.info('searching for <py-config>');
50+
const el = document.querySelector('py-config');
51+
this.config = loadConfigFromElement(el);
52+
logger.info('config loaded:\n' + JSON.stringify(this.config, null, 2));
53+
}
54+
55+
initialize() {
56+
addInitializer(this.loadPackages);
57+
addInitializer(this.loadPaths);
58+
this.loadRuntimes();
59+
}
60+
61+
loadPackages = async () => {
62+
logger.info("Packages to install: ", this.config.packages);
63+
await runtimeSpec.installPackage(this.config.packages);
64+
}
65+
66+
loadPaths = async () => {
67+
const paths = this.config.paths;
68+
logger.info("Paths to load: ", paths)
69+
for (const singleFile of paths) {
70+
logger.info(` loading path: ${singleFile}`);
71+
try {
72+
await runtimeSpec.loadFromFile(singleFile);
73+
} catch (e) {
74+
//Should we still export full error contents to console?
75+
handleFetchError(<Error>e, singleFile);
76+
}
77+
}
78+
logger.info("All paths loaded");
79+
}
80+
81+
loadRuntimes() {
82+
logger.info('Initializing runtimes');
83+
for (const runtime of this.config.runtimes) {
84+
const runtimeObj: Runtime = new PyodideRuntime(this.config, runtime.src,
85+
runtime.name, runtime.lang);
86+
const script = document.createElement('script'); // create a script DOM node
87+
script.src = runtimeObj.src; // set its src to the provided URL
88+
script.addEventListener('load', () => {
89+
void runtimeObj.initialize();
90+
});
91+
document.head.appendChild(script);
92+
}
93+
}
94+
95+
}
96+
97+
function pyscript_get_config() {
98+
return globalApp.config;
2599
}
100+
globalExport('pyscript_get_config', pyscript_get_config);
26101

27-
// add loader to the page body
28-
logger.info('add py-loader');
29-
const loader = <PyLoader>document.createElement('py-loader');
30-
document.body.append(loader);
31-
globalLoader.set(loader);
102+
// main entry point of execution
103+
const globalApp = new PyScriptApp();
104+
globalApp.main();

0 commit comments

Comments
 (0)
0