1
1
"use strict" ;
2
+ /******************************************************************************
3
+ MicroPyscript.
4
+
5
+ A small, simple, single file kernel of PyScript, made for testing purposes.
6
+
7
+ See the README for more details, design decisions, and an explanation of how
8
+ things work.
9
+
10
+ Authors:
11
+ - Nicholas H.Tollervey (ntollervey@anaconda.org)
12
+
13
+ Copyright (c) 2022 Anaconda Inc.
14
+
15
+ Licensed under the Apache License, Version 2.0 (the "License");
16
+ you may not use this file except in compliance with the License.
17
+ You may obtain a copy of the License at
18
+
19
+ http://www.apache.org/licenses/LICENSE-2.0
20
+
21
+ Unless required by applicable law or agreed to in writing, software
22
+ distributed under the License is distributed on an "AS IS" BASIS,
23
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
+ See the License for the specific language governing permissions and
25
+ limitations under the License.
26
+ ******************************************************************************/
27
+
2
28
3
29
/******************************************************************************
4
- Base classes.
30
+ Base classes and constants .
5
31
******************************************************************************/
6
32
class Plugin {
7
33
/*
@@ -94,32 +120,34 @@ class Runtime {
94
120
}
95
121
}
96
122
123
+
124
+ const splashInnerHTML = '<svg class="whole" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" height="48" width="48"><g id="loader"><animateTransform xlink:href="#loader" attributeName="transform" attributeType="XML" type="rotate" from="0 50 50" to="360 50 50" dur="1s" begin="0s" repeatCount="indefinite" restart="always"></animateTransform><path class="a" opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M50 100C77.6142 100 100 77.6142 100 50C100 22.3858 77.6142 0 50 0C22.3858 0 0 22.3858 0 50C0 77.6142 22.3858 100 50 100ZM50 90C72.0914 90 90 72.0914 90 50C90 27.9086 72.0914 10 50 10C27.9086 10 10 27.9086 10 50C10 72.0914 27.9086 90 50 90Z" fill="#999999"></path><path class="b" fill-rule="evenodd" clip-rule="evenodd" d="M100 50C100 22.3858 77.6142 0 50 0V10C72.0914 10 90 27.9086 90 50H100Z" fill="#999999"></path></g></svg>' ;
125
+
126
+
97
127
/******************************************************************************
98
128
Built-in plugins and runtimes.
99
129
******************************************************************************/
100
-
101
130
class PyScriptTag extends Plugin {
102
131
start ( config ) {
103
132
// Define the PyScript element.
104
133
class PyScript extends HTMLElement {
105
134
connectedCallback ( ) {
106
135
/*
107
- All code is dispatched as a " py-code" event with code for later
108
- processing.
136
+ All code is dispatched as a py-script-registered event
137
+ for later processing.
109
138
110
139
Additional metadata if available:
111
- - this element's id
112
- - the src value
140
+ - the src value for remote source file
141
+ - this element as target
113
142
*/
114
- var code = this . textContent ;
143
+ const code = this . textContent ;
115
144
this . textContent = "" ;
116
- var detail = { }
117
- detail . code = code . trim ( ) ? code : "" ;
118
- if ( this . attributes . src ) {
119
- detail . src = this . attributes . src . value ;
120
- }
121
- detail . target = this ;
122
- const pyScriptRegistered = new CustomEvent ( "py-script-registered" , { "detail" : detail } ) ;
145
+ const script = {
146
+ code : code . trim ( ) ? code : "" ,
147
+ src : this . attributes . src ? this . attributes . src . value : "" ,
148
+ target : this
149
+ } ;
150
+ const pyScriptRegistered = new CustomEvent ( "py-script-registered" , { "detail" : script } ) ;
123
151
document . dispatchEvent ( pyScriptRegistered ) ;
124
152
}
125
153
}
@@ -146,6 +174,7 @@ class MicroPythonRuntime extends Runtime {
146
174
if ( config . mp_memory ) {
147
175
mp_memory = config . mp_memory ;
148
176
}
177
+ // TODO: Fix this.
149
178
mp_js_stdout . addEventListener ( 'print' , function ( e ) {
150
179
this . innerText = this . innerText + e . data ;
151
180
} , false ) ;
@@ -189,28 +218,33 @@ class CPythonRuntime extends Runtime {
189
218
}
190
219
}
191
220
221
+
192
222
/******************************************************************************
193
223
The core PyScript app definition.
194
224
******************************************************************************/
195
-
196
225
const main = function ( ) {
197
226
// Really simple logging. Emoji 🐍 highlights PyScript app logs. ;-)
198
227
const logger = function ( ) {
199
228
return Function . prototype . bind . call ( console . log , console , "🐍 " , ...arguments ) ;
200
229
} ( ) ;
201
230
logger ( "Starting PyScript. 👋" )
231
+
202
232
// Default configuration settings for PyScript. These may be overridden by
203
233
// the app.loadConfig function.
204
234
const config = {
205
235
"runtime" : "micropython" // Numpty default.
206
236
}
237
+
207
238
// Contains plugins to the PyScript context.
208
239
const plugins = [ ] ;
240
+
209
241
// Contains Python scripts found on the page.
210
242
const scripts = [ ] ;
243
+
211
244
// Contains Python scripts whose source code is available, and pending
212
245
// evaluation by the runtime.
213
246
const pendingScripts = [ ] ;
247
+
214
248
// Details of runtimes.
215
249
// Key: lowercase runtime name.
216
250
// Value: the class wrapping that version of the runtime.
@@ -220,9 +254,11 @@ const main = function() {
220
254
}
221
255
// Default to smallest/fastest runtime.
222
256
runtimes [ "default" ] = runtimes [ "micropython" ]
257
+
223
258
// Eventually references an instance of the Runtime class, representing the
224
259
// started runtime.
225
260
let runtime = null ;
261
+
226
262
// Flag to indicate the runtime is ready to evaluate scripts.
227
263
let runtimeReady = false ;
228
264
@@ -298,15 +334,15 @@ const main = function() {
298
334
} ,
299
335
runtimeStarted : function ( ) {
300
336
/*
301
- The runtime is ready to go, so flip the runtimeReady flag and begin
337
+ The runtime is ready to go, so flip the runtimeReady flag, step
338
+ through each registered plugin's onRuntimeReady method, and begin
302
339
evaluating any code in the pendingScripts queue.
303
340
*/
304
341
logger ( `Runtime started. 🎬` )
305
342
runtimeReady = true ;
306
343
plugins . forEach ( function ( plugin ) {
307
- plugin . onRuntimeReady ( config ) ;
344
+ plugin . onRuntimeReady ( config , runtime ) ;
308
345
} ) ;
309
- pendingScripts . reverse ( ) ;
310
346
pendingScripts . forEach ( function ( script ) {
311
347
const pyEvalScript = new CustomEvent ( "py-eval-script" , { detail : script } ) ;
312
348
document . dispatchEvent ( pyEvalScript ) ;
@@ -316,8 +352,8 @@ const main = function() {
316
352
} ,
317
353
registerScript ( script ) {
318
354
/*
319
- Add a Python script to the scripts array, to be run when the
320
- runtime is ready .
355
+ Add a Python script to the scripts array. If required load the code
356
+ by fetching it from the URL found in the script's src attribute .
321
357
*/
322
358
// Ignore code that is just whitespace.
323
359
script . code = script . code . trim ( ) ? script . code : "" ;
@@ -331,7 +367,7 @@ const main = function() {
331
367
// Handle asynchronous loading of the script's code from the
332
368
// URL in src.
333
369
fetch ( script . src ) . then ( function ( response ) {
334
- logger ( `Fetch script from "${ script . src } " 📡` , response ) ;
370
+ logger ( `Fetched script from "${ script . src } " 📡` , response ) ;
335
371
if ( response . ok ) {
336
372
response . text ( ) . then ( ( data ) => {
337
373
script . code = data ;
@@ -347,14 +383,14 @@ const main = function() {
347
383
} else {
348
384
// Warn that a script has no source code either inline or via
349
385
// the src attribute.
350
- logger ( "Script has no source code. ⁉️" , script ) ;
386
+ logger ( "Script has no source code. ⁉️😕 " , script ) ;
351
387
}
352
388
} ,
353
389
loadScript ( script ) {
354
390
/*
355
- Ensure the source code for all the scripts is available. For any
356
- code that has a src but no content, will fetch the code from the
357
- URL in src. Dispatches a py-scripts-loaded event when done .
391
+ The given script is either queued for later evaluation if the
392
+ runtime isn't ready yet, or the py-eval-script event is
393
+ dispatched so the runtime can evaluate it .
358
394
*/
359
395
if ( runtimeReady ) {
360
396
// Runtime is ready, so evaluate the code.
@@ -376,19 +412,22 @@ const main = function() {
376
412
} ,
377
413
}
378
414
379
- // The following functions are used to coordinate the unfolding of PyScript
380
- // as various events are dispatched and state evolves to trigger the next
381
- // steps.
415
+
416
+ // The following functions coordinate the unfolding of PyScript as various
417
+ // events are dispatched and state evolves to trigger the next steps.
382
418
//
383
419
// These functions are defined in the order they're roughly expected to
384
420
// be called through the life-cycle of the page, although this cannot be
385
421
// guaranteed for some of the functions.
386
-
387
422
function onPyConfigured ( e ) {
388
423
/*
389
- Once configured, load the runtime, register the default plugins
390
- (currently only the PyScriptTag), freeze the config and start the
391
- plugins to kick off extracting Python scripts from the page.
424
+ Once PyScript has loaded its configuration:
425
+ - register the default plugins (currently only PyScriptTag), so
426
+ they can modify the config if required.
427
+ - freeze the config so it can't be changed from this point.
428
+ - load the Python runtime into the browser.
429
+ - start the plugins to kick off extracting Python scripts from the
430
+ page.
392
431
*/
393
432
app . registerPlugin ( new PyScriptTag ( ) ) ;
394
433
Object . freeze ( config ) ;
@@ -398,32 +437,42 @@ const main = function() {
398
437
}
399
438
document . addEventListener ( "py-configured" , onPyConfigured ) ;
400
439
440
+
401
441
function onPyScriptRegistered ( e ) {
402
442
/*
403
- Register a Python script and related metadatacontained in the
404
- dispatched event's detail.
443
+ A plugin has, in some way, detected a Python script definition.
444
+
445
+ Register metadata about the script via the dispatched event's detail.
405
446
*/
406
447
app . registerScript ( e . detail ) ;
407
448
}
408
449
document . addEventListener ( "py-script-registered" , onPyScriptRegistered ) ;
409
450
451
+
410
452
function onPyScriptLoaded ( e ) {
411
453
/*
412
- The source of a Python script is available as metadata in the
413
- dispatched event's detail.
454
+ The source of a Python script has been obtained either as inline code
455
+ or as the content of a remote Python source file that has been fetched
456
+ over the network.
457
+
458
+ The source code is included as metadata in the dispatched event's
459
+ detail. So signal to the app the script is fully loaded.
414
460
*/
415
461
app . loadScript ( e . detail ) ;
416
462
}
417
463
document . addEventListener ( "py-script-loaded" , onPyScriptLoaded ) ;
418
464
465
+
419
466
function onRuntimeLoaded ( e ) {
420
467
/*
421
- The runtime has loaded.
468
+ The runtime has loaded over the network. Next, start the runtime in
469
+ this PyScript context.
422
470
*/
423
471
app . startRuntime ( ) ;
424
472
}
425
473
document . addEventListener ( "py-runtime-loaded" , onRuntimeLoaded ) ;
426
474
475
+
427
476
function onRuntimeReady ( e ) {
428
477
/*
429
478
The runtime is ready to evaluate scripts.
@@ -432,27 +481,28 @@ const main = function() {
432
481
}
433
482
document . addEventListener ( "py-runtime-ready" , onRuntimeReady ) ;
434
483
484
+
435
485
function onEvalScript ( e ) {
436
486
/*
437
- The runtime is ready, and a script's source code is ready, so evaluate
438
- the script with the runtime!
487
+ Handle the event designating a script is ready to be evaluated by the
488
+ runtime.
439
489
*/
440
490
app . evaluateScript ( e . detail )
441
491
}
442
492
document . addEventListener ( "py-eval-script" , onEvalScript ) ;
443
493
444
- // Finally, return a function to start PyScript.
445
494
495
+ // Finally, return a function to start PyScript.
446
496
return function ( ) {
447
- /*
448
- Start PyScript.
449
- */
450
- // TODO: check test/debug flag.
451
- app . loadConfig ( ) ;
497
+ // Check to bypass loadConfig, for testing purposes.
498
+ if ( ! window . pyscriptTest ) {
499
+ app . loadConfig ( ) ;
500
+ }
452
501
return app ;
453
502
}
454
503
} ( ) ;
455
504
505
+
456
506
/******************************************************************************
457
507
Start PyScript.
458
508
******************************************************************************/
0 commit comments