1
1
# MicroPyScript 🔬 🐍
2
2
3
- A small, simple kernel of PyScript, made for testing purposes.
3
+ A small, simple kernel of PyScript, made for testing purposes in the spirit of
4
+ a [ code spike] ( https://en.wikipedia.org/wiki/Spike_(software_development) ) .
4
5
5
6
This is the way:
6
7
@@ -10,15 +11,15 @@ This is the way:
10
11
* Vanilla JavaScript.
11
12
* Pluggable.
12
13
* Comments.
13
- * Tests .
14
+ * (Some) tests .
14
15
* Build for change.
15
16
16
- This is a solid foundation for lightweight testing of Python runtimes that
17
+ This is a foundation for lightweight testing of Python interpreters that
17
18
target WASM. Inspired by code in the "real" PyScript website and our plans for
18
19
plugins and simple event based coordination.
19
20
20
21
Complexity, edge cases and customization is (hopefully) confined to plugins and
21
- bespoke runtimes .
22
+ bespoke interpreters .
22
23
23
24
That is all.
24
25
@@ -56,26 +57,16 @@ To check things are working:
56
57
$ make serve
57
58
```
58
59
59
- Then point your browser to http://0.0.0.0:8000/ to see a "Hello World" from
60
- MicroPython.
61
-
62
- ### What's in the files?
63
-
64
- * ** README.md** - this file, containing project documentation.
65
- * ** Makefile** - common tasks scripted into convenient targets. See above.
66
- * ** hello.py** - a simple "hello world" Python script for PyScript to run.
67
- * ** index.html** - a small web page that uses PyScript.
68
- * ** pyscript.js** - the simple, single file implementation of PyScript.
69
- * ** SpecRunner.html** - a web page to run the test specifications with Jasmine.
70
- * ** spec** - a directory containing the test specifications for Jasmine.
71
- * ** lib** - a directory containing the Jasmine test library.
72
-
73
- To change the configuration of PyScript take a look at the JSON object
74
- defined in the ` <py-config> ` tag in ` index.html ` . Currently valid runtimes are
75
- ` micropython ` or ` pyodide ` .
60
+ Then point your browser to http://0.0.0.0:8000/ to see the first page of an
61
+ interactive technical report about using MicroPython. You should be able to
62
+ change the interpreter from ` micropython ` to ` pyodide ` and things should just
63
+ work as before, but with a different interpreter at the bottom of the PyScript
64
+ stack.
76
65
77
66
## Running the tests
78
67
68
+ ** TESTS ARE CURRENTLY BROKEN**
69
+
79
70
For the sake of simplicity (and familiarity) we use the
80
71
[ Jasmine test framework] ( https://jasmine.github.io/index.html ) to exercise the
81
72
JavaScript aspects of our code.
@@ -86,43 +77,56 @@ and run the Jasmine based test suite.
86
77
87
78
## How it works
88
79
89
- The PyScript core only loads configuration, starts the Python runtime and
90
- allows the registration of plugins. All other logic, capabilities and features
91
- are contained in the plugins.
80
+ The PyScript core only loads configuration, starts the Python interpreter,
81
+ allows the registration of plugins and adds files to the interpreter's
82
+ filesystem. All other logic, capabilities and features are contained in the
83
+ plugins.
84
+
85
+ Currently, only two plugins are provided:
92
86
93
- Currently, only a single plugin is provided by PyScript: the one that
94
- implements the core ` <py-script> ` tag.
87
+ * One built into PyScript that implements the core ` <py-script> ` tag.
88
+ * The other (in ` customtags.js ` ) implements the ` <py-repl> ` tag to demonstrate
89
+ a "third party" plugin.
95
90
96
91
The story of PyScript's execution is roughly as follows:
97
92
98
93
1 . Configuration is loaded from the ` <py-config> ` tag. Once complete the
99
94
` py-configured ` event is dispatched, containing the ` config ` object based
100
95
upon default values overridden by the content of the ` <py-config> ` tag.
101
- 2 . When the ` py-configured ` event is dispatched two things happen:
102
- * The runtime is loaded via injecting a ` <script> ` tag that references the
103
- runtime 's URL. Once loaded the ` py-runtime -loaded ` event is dispatched.
96
+ 2 . When the ` py-configured ` event is dispatched three things happen:
97
+ * The interpreter is loaded via injecting a ` <script> ` tag that references the
98
+ interpreter 's URL. Once loaded the ` py-interpreter -loaded ` event is dispatched.
104
99
* Plugins are registered and have their ` configure ` function called. For each
105
100
plugin registered a ` py-plugin-registered ` event is dispatched, containing
106
101
the (potentially changed) ` config ` , and a reference to the newly registered
107
102
plugin.
108
- 3 . When ` py-runtime-loaded ` is dispatched two things happen:
109
- * The runtime is instantiated / started. Once complete the ` py-runtime-ready `
103
+ * The content of the files to be added to the interpreter's filesystem are
104
+ fetched. Once downloaded each file causes a ` py-file-fetched ` event to be
105
+ dispatched with the path and content of the file attached to it.
106
+ 3 . When ` py-interpreter-loaded ` is dispatched two things happen:
107
+ * The interpreter is instantiated / started. Once complete the ` py-interpreter-ready `
110
108
event is dispatched.
111
109
* All registered plugins have their ` start ` function called and a
112
110
` py-plugin-started ` event is dispatched for each plugin.
113
- 4 . When the ` py-runtime-ready ` event is dispatched all plugins have their
114
- ` onRuntimeReady ` function called with the ` config ` and ` runtime ` objects.
115
- 5 . Any plugins registered after the runtime is ready immediately have their
116
- ` configure ` , ` start ` and ` onRuntimeReady ` functions called, with the
111
+ 4 . When the ` py-interpreter-ready ` event is dispatched all plugins have their
112
+ ` onInterpreterReady ` function called with the ` config ` and ` interpreter `
113
+ objects. At this point all files are copied onto the interpreter's
114
+ filesystem. When all the files are copied the ` py-files-loaded ` event is
115
+ dispatched.
116
+ 5 . When both the interpreter and filesystem are finished setting up and in a
117
+ ready state, the ` py-finished-setup ` event is dispatched to signal PyScript
118
+ is ready to evaluate user's code.
119
+ 6 . Any plugins registered after the interpreter is ready immediately have their
120
+ ` configure ` , ` start ` and ` onInterpreterReady ` functions called, with the
117
121
` py-plugin-registered ` and ` py-plugin-started ` events being dispatched.
118
122
119
123
That's it!
120
124
121
125
When ` pyscript.js ` is run, it creates a ` window.PyScript ` object that contains
122
126
read-only references to the ` config ` , registered ` plugins ` ,
123
- ` availableRuntimes ` , the ` runtime ` used on the page, an ` isRuntimeReady ` flag,
124
- a ` registerPlugin ` function (see below) and a ` runPython(code) ` function that
125
- takes a string of Python.
127
+ ` availableInterpreters ` , the ` interpreter ` used on the page, a
128
+ n ` isInterpreterReady ` flag, a ` registerPlugin ` function (see below) and a
129
+ ` runPython(code) ` function that takes a string of Python.
126
130
127
131
There are copious comments in the ` pyscript.js ` file. My intention is for
128
132
simplicity, lack of onerous dependencies (bye-bye ` npm ` ), and
@@ -157,8 +161,8 @@ following order (as the lifecycle of the plugin):
157
161
should not be modified by the plugin. Example use cases:
158
162
- Define custom HTML elements.
159
163
- Start fetching external resources.
160
- * ` onRuntimeReady (config, runtime )` - Called once the runtime is ready to
161
- evaluate Python code. Example use cases:
164
+ * ` onInterpreterReady (config, interpreter )` - Called once the interpreter is
165
+ ready to evaluate Python code. Example use cases:
162
166
- ` pip install ` packages.
163
167
- Import/initialize Python plugins.
164
168
@@ -169,11 +173,11 @@ The following events, dispatched by PyScript itself, are related to plugins:
169
173
immediately after the plugin's ` configure ` function is called.
170
174
* ` py-plugin-started ` - Dispatched immediately after a plugin's ` start `
171
175
function is called. The event contains a reference to the started plugin.
172
- * ` py-runtime -ready ` - causes each plugin's ` onRuntimeReady ` function to be
173
- called.
176
+ * ` py-interpreter -ready ` - causes each plugin's ` onInterpreterReady ` function
177
+ to be called.
174
178
175
- If a plugin is registered * after* the runtime is ready, all three functions are
176
- immediately called in the expected sequence, one after the other.
179
+ If a plugin is registered * after* the interpreter is ready, all three functions
180
+ are immediately called in the expected sequence, one after the other.
177
181
178
182
The recommended way to create and register plugins is:
179
183
@@ -202,14 +206,15 @@ const myPlugin = function(e) {
202
206
*/
203
207
204
208
const plugin = {
209
+ name: " my-plugin" ,
205
210
configure : function (config ) {
206
211
// ...
207
212
},
208
213
start : function (config ) {
209
214
// ...
210
215
foo ();
211
216
},
212
- onRuntimeReady : function (config , runtime ) {
217
+ onInterpreterReady : function (config , interpreter ) {
213
218
// ...
214
219
}
215
220
};
@@ -226,44 +231,41 @@ Then in your HTML file:
226
231
<script src =" pyscript.js" type =" module" ></script >
227
232
```
228
233
229
- A good example of a plugin is the built-in plugin for the ` <py-script> ` tag
230
- found in ` pyscript.js ` (search for the object assigned to ` pyScriptTag ` ).
231
-
232
- ## Runtimes
234
+ ## Interpreters
233
235
234
- The ` Runtime ` class abstracts away all the implementation details of the
235
- various Python runtimes we might use.
236
+ The ` Interpreter ` class abstracts away all the implementation details of the
237
+ various Python interpreters we might use.
236
238
237
- To see a complete implementation see the ` MicroPythonRuntime ` class that
238
- inherits from ` Runtime ` . There is also an incomplete ` PyodideRuntime ` class so
239
- I was able to compare and contrast the differences between implementations and
240
- arrive at a general abstraction (still very much a work in progress). Comments
241
- in the code should explain what's going on in terms of the life-cycle and
242
- capabilities of a "runtime ".
239
+ To see a complete implementation see the ` MicroPythonInterpreter ` class that
240
+ inherits from ` Interpreter ` . There is also an incomplete ` PyodideInterpreter `
241
+ class so I was able to compare and contrast the differences between
242
+ implementations and arrive at a general abstraction (still very much a work in
243
+ progress). Comments in the code should explain what's going on in terms of the
244
+ life-cycle and capabilities of a "interpreter ".
243
245
244
- The afore mentioned ` MicroPythonRuntime ` , ` CPythonRuntime ` and ` PyodideRuntime `
245
- all, to a greater or lesser extent, define a uniform shim around their
246
- respective runtimes . The MicroPython one is most complete, but still needs work
247
- as I make changes to how MicroPython itself exposes ` stdout ` , ` stderr ` and
248
- consumes ` stdin ` .
246
+ The afore mentioned ` MicroPythonInterpreter ` , ` CPythonInterpreter ` and
247
+ ` PyodideInterpreter ` all, to a greater or lesser extent, define a uniform shim
248
+ around their respective interpreter . The MicroPython one is most complete, but
249
+ still needs work as I make changes to how MicroPython itself exposes ` stdout ` ,
250
+ ` stderr ` and consumes ` stdin ` .
249
251
250
252
## The future
251
253
252
- Who knows..? But this is a good scaffold for testing different Python runtimes.
254
+ Who knows..? But this is a good scaffold for testing different Python
255
+ interpreters.
253
256
254
257
Next steps:
255
258
256
259
* More comprehensive tests.
257
- * ` CPythonRuntime ` fully implemented.
258
- * ` PyodideRuntime ` finished.
259
- * ` MicroPythonRuntime ` refactored after making MicroPython play nicer with
260
+ * ` CPythonInterpreter ` fully implemented.
261
+ * ` PyodideInterpreter ` finished.
262
+ * ` MicroPythonInterpreter ` refactored after making MicroPython play nicer with
260
263
` stdout ` and ` stderr ` .
261
- * A plugin for a ` <py-repl> ` tag (the foundations are in place).
262
- * A uniform way to ` pip install ` packages in each runtime.
263
- * A uniform JavaScript gateway from within each runtime.
264
+ * A uniform way to ` pip install ` packages in each interpreter.
265
+ * A uniform JavaScript gateway from within each interpreter.
264
266
* A uniform ` navigator ` object through which to access the DOM from within each
265
- runtime .
267
+ interpreter .
266
268
* Running in web-workers (and associated message passing work), for each
267
- runtime .
269
+ interpreter .
268
270
269
271
That's it..! ;-)
0 commit comments