Description
TL;DR: I'm proposing to model the life cycle of a PyScript page using a plugin system. The main logic is very slim, it calls hooks at well defined times, and most of the functionality is implemented inside plugins, some of which are builtin. This is loosely modeled after what e.g. pytest
does, but I'm sure there are other systems following a similar model
In order to design the new system, we need to first analyze what is the current flow of execution. To avoid having a giant wall of text, I'm going to analyze the current status in this post, and write a proposal for the refactoring in the next one.
Current control flow
-
The HTML page is parsed. We encounter
<script defer src=".../pyscript.js>
Because ofdefer
, the script is executed when the document is fully parsed. -
pyscript.js is executed. We start from
main.ts
:pyscript/pyscriptjs/src/main.ts
Lines 1 to 43 in 6023c41
-
main.ts imports a bunch of modules; in particular,
pyscript.ts
has also some module-level statements which calladdInitializer
andaddPostInitializer
:
pyscript/pyscriptjs/src/components/pyscript.ts
Lines 265 to 266 in 6023c41
-
we call
customElements.define
for all CE. As soon as each of them is defined, all the tags in the document are connected. This means that the order of execution of the callbacks might be different than the order of the tags in the HTML. Related issue: <py-script>, <py-button> and <py-inputbox> etc. are executed in a non-obvious order #761.
Note: that at this point in time nothing else is loaded yet; no runtime, nopy-config
, nopy-env
, etc.
Scripts are enqueued but not executed.
pyscript/pyscriptjs/src/main.ts
Lines 19 to 28 in 6023c41
-
The various connectedCallback are called. In the following, we say "DOM logic" to mean all the logic which aims at locating/adding/modifying elements in the DOM, e.g. to create the
<div>
to hold the output of a script:- PyScript: some immediate DOM logic, then it calls
addToScriptsQueue
- PyRepl: only immadiate DOM logic
- PyEnv: very few logic done immediately; it uses
addInitializer
to callloadEnv
andloadPaths
later - PyBox, PyTitle: only immadiate DOM logic
- PyButton, PyInputBox: immediate DOM logic +
this.runAfterRuntimeInitialized
(which usesruntimeLoaded.subscribe
) - PyWidget: after a bit of indirections, it ends up calling
runtimeLoaded.subscribe
, but then in the callback we use a 1sec delay
before actually instantiatingproxyClass
(which is a python class) - PyConfig: see later
- PyScript: some immediate DOM logic, then it calls
-
after the imports, there is some core logic in main.ts: we create a
<py-config>
element if needed. So precise timing at whichPyConfig.connectedCallback
happens depends on whether there is a real tag or not:- if there was a
<py-config>
tag, it was called at point (4) - else, it is called now.
- if there was a
-
pyconfig.connectedCallback
calls loadRuntimes(), which create a<script>
tag to download pyodide, and add an event listener for itsload()
event:
pyscript/pyscriptjs/src/components/pyconfig.ts
Lines 62 to 73 in 6023c41
-
we continue the main flow of execution: in main.ts we add the
<py-loader>
element to the DOM; the splashscreen is showed only now. main.ts finally finishes execution:
pyscript/pyscriptjs/src/main.ts
Lines 39 to 43 in 6023c41
-
eventually the
<script>
tag added at point (7) finishes loading, and it fires theload
event (which is implemented insidepyconfig.ts:loadRuntimes
). We callruntimeObj.initialize()
. The execution flow bounces back and forth between the base class in runtime.ts and the concrete subclass inpyodide.ts
. We callloadInterpreter
.
pyscript/pyscriptjs/src/runtime.ts
Lines 135 to 181 in 6023c41
-
We call all the things which were enqueued earlier. We start with
initializers
, which currently are:-
pyscript.ts:mountElements
:pyscript/pyscriptjs/src/components/pyscript.ts
Lines 254 to 265 in 6023c41
-
pyenv.ts:loadEnv
andloadPaths
:pyscript/pyscriptjs/src/components/pyenv.ts
Lines 59 to 79 in 6023c41
-
-
we execute
<py-script>
tags:pyscript/pyscriptjs/src/runtime.ts
Lines 161 to 165 in 6023c41
-
We close the loader (why so early?):
pyscript/pyscriptjs/src/runtime.ts
Lines 170 to 172 in 6023c41
-
we run
postInitializers
. Currently the only one isinitHandlers
:pyscript/pyscriptjs/src/components/pyscript.ts
Lines 213 to 219 in 6023c41
-
at some point, we also run the callbacks registered by using
runAfterRuntimeInitialized
, which currently is invoked by:- pybutton.ts
- pyinputbox.ts
- in both cases above, what it does is to run further user-define code inside e.g. the
<py-button>
tag, but using a different way than for<py-script>
:pyscript/pyscriptjs/src/components/pybutton.ts
Lines 69 to 73 in 6023c41
Metadata
Metadata
Assignees
Labels
Type
Projects
Status