q5 4.5.0 → 4.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deno.json +2 -1
- package/package.json +28 -5
- package/q5.d.ts +3 -1
- package/q5.js +163 -80
- package/q5.min.js +2 -1
- package/q5.min.js.map +1 -0
- package/.prettierignore +0 -2
- package/teach/accessibility.html +0 -503
package/deno.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@q5/q5",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.3",
|
|
4
4
|
"license": "LGPL-3.0-only",
|
|
5
5
|
"description": "Beginner friendly graphics powered by WebGPU, optimized for interactive art!",
|
|
6
6
|
"author": "quinton-ashley",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"home",
|
|
25
25
|
"lang",
|
|
26
26
|
"learn",
|
|
27
|
+
"teach",
|
|
27
28
|
"test",
|
|
28
29
|
".npmignore",
|
|
29
30
|
".prettierignore",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "q5",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.3",
|
|
4
4
|
"description": "Beginner friendly graphics powered by WebGPU, optimized for interactive art!",
|
|
5
5
|
"author": "quinton-ashley",
|
|
6
6
|
"contributors": [
|
|
@@ -12,8 +12,27 @@
|
|
|
12
12
|
],
|
|
13
13
|
"license": "LGPL-3.0-only",
|
|
14
14
|
"homepage": "https://q5js.org/home",
|
|
15
|
-
"main": "q5-server.js",
|
|
16
15
|
"types": "q5.d.ts",
|
|
16
|
+
"browser": {
|
|
17
|
+
".": "./q5.js",
|
|
18
|
+
"node:process": false
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./q5.d.ts",
|
|
23
|
+
"browser": "./q5.js",
|
|
24
|
+
"deno": "./q5-deno-server.js",
|
|
25
|
+
"node": "./q5-server.js",
|
|
26
|
+
"default": "./q5.js"
|
|
27
|
+
},
|
|
28
|
+
"./q5.min.js": "./q5.min.js",
|
|
29
|
+
"./q5.js": "./q5.js",
|
|
30
|
+
"./q5-server.js": {
|
|
31
|
+
"browser": null,
|
|
32
|
+
"deno": "./q5-deno-server.js",
|
|
33
|
+
"default": "./q5-server.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
17
36
|
"funding": [
|
|
18
37
|
{
|
|
19
38
|
"type": "patreon",
|
|
@@ -30,7 +49,7 @@
|
|
|
30
49
|
],
|
|
31
50
|
"scripts": {
|
|
32
51
|
"bundle": "cat src/q5-core.js src/q5-canvas.js src/q5-c2d-canvas.js src/q5-c2d-shapes.js src/q5-c2d-image.js src/q5-c2d-soft-filters.js src/q5-c2d-text.js src/q5-color.js src/q5-display.js src/q5-dom.js src/q5-fes.js src/q5-input.js src/q5-math.js src/q5-record.js src/q5-sound.js src/q5-util.js src/q5-vector.js src/q5-webgpu.js src/q5-lang.js src/q5-python.js > q5.js",
|
|
33
|
-
"min": "terser q5.js --compress ecma=
|
|
52
|
+
"min": "terser q5.js --compress ecma=2026 --mangle --output q5.min.js --source-map url=q5.min.js.map",
|
|
34
53
|
"dist": "bun bundle && bun min",
|
|
35
54
|
"tests": "jest test",
|
|
36
55
|
"bld": "node lang/build.js",
|
|
@@ -61,6 +80,10 @@
|
|
|
61
80
|
"jest-cli": "^29.7.0",
|
|
62
81
|
"jsdom": "^25.0.1",
|
|
63
82
|
"json2csv": "^6.0.0-alpha.2",
|
|
64
|
-
"skia-canvas": "^1.0.2"
|
|
65
|
-
|
|
83
|
+
"skia-canvas": "^1.0.2",
|
|
84
|
+
"terser": "^5.46.1"
|
|
85
|
+
},
|
|
86
|
+
"trustedDependencies": [
|
|
87
|
+
"skia-canvas"
|
|
88
|
+
]
|
|
66
89
|
}
|
package/q5.d.ts
CHANGED
|
@@ -2500,6 +2500,8 @@ declare global {
|
|
|
2500
2500
|
* - If one numerical input is provided, returns a number between 0 and the provided value.
|
|
2501
2501
|
* - If two numerical inputs are provided, returns a number between the two values.
|
|
2502
2502
|
* - If an array is provided, returns a random element from the array.
|
|
2503
|
+
*
|
|
2504
|
+
* Return value can be the lower bound but can never exactly be the upper bound.
|
|
2503
2505
|
* @param {number | any[]} [low] lower bound (inclusive) or an array
|
|
2504
2506
|
* @param {number} [high] upper bound (exclusive)
|
|
2505
2507
|
* @returns {number | any} a random number or element
|
|
@@ -4035,7 +4037,7 @@ declare global {
|
|
|
4035
4037
|
*
|
|
4036
4038
|
* q5.draw = function () {
|
|
4037
4039
|
* shader(stripes);
|
|
4038
|
-
*
|
|
4040
|
+
* plane(0, 0, width, height);
|
|
4039
4041
|
*
|
|
4040
4042
|
* resetShader();
|
|
4041
4043
|
* triangle(-50, -50, 0, 50, 50, -50);
|
package/q5.js
CHANGED
|
@@ -308,9 +308,9 @@ function Q5(scope, parent, renderer) {
|
|
|
308
308
|
|
|
309
309
|
function wrapWithFES(name) {
|
|
310
310
|
const fn = t[name] || $[name];
|
|
311
|
-
$[name] = (
|
|
311
|
+
$[name] = function (...args) {
|
|
312
312
|
try {
|
|
313
|
-
return fn(
|
|
313
|
+
return fn.apply(this, args);
|
|
314
314
|
} catch (e) {
|
|
315
315
|
if ($._fes) $._fes(e);
|
|
316
316
|
throw e;
|
|
@@ -322,6 +322,7 @@ function Q5(scope, parent, renderer) {
|
|
|
322
322
|
await runHooks('presetup');
|
|
323
323
|
|
|
324
324
|
readyResolve();
|
|
325
|
+
if ($._removed) return;
|
|
325
326
|
|
|
326
327
|
if (t.preload || $.preload) {
|
|
327
328
|
wrapWithFES('preload');
|
|
@@ -353,9 +354,9 @@ function Q5(scope, parent, renderer) {
|
|
|
353
354
|
})
|
|
354
355
|
]);
|
|
355
356
|
|
|
356
|
-
if (
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
if ($._removed) return;
|
|
358
|
+
if (!$._disablePreload) await $.loadAll();
|
|
359
|
+
if ($._removed) return;
|
|
359
360
|
|
|
360
361
|
$.setup ??= t.setup || (() => {});
|
|
361
362
|
wrapWithFES('setup');
|
|
@@ -367,10 +368,12 @@ function Q5(scope, parent, renderer) {
|
|
|
367
368
|
millisStart = performance.now();
|
|
368
369
|
await $.setup();
|
|
369
370
|
$._setupDone = true;
|
|
371
|
+
|
|
372
|
+
if ($._removed) return;
|
|
370
373
|
if ($.ctx === null) $.createCanvas(200, 200);
|
|
371
374
|
await runHooks('postsetup');
|
|
372
375
|
|
|
373
|
-
if ($.frameCount) return;
|
|
376
|
+
if ($.frameCount || $._removed) return;
|
|
374
377
|
|
|
375
378
|
$._lastFrameTime = performance.now() - 15;
|
|
376
379
|
raf(_draw);
|
|
@@ -450,7 +453,7 @@ Q5.preloadMethods = {};
|
|
|
450
453
|
Q5.prototype.registerPreloadMethod = (n, fn) => (Q5.preloadMethods[n] = fn[n]);
|
|
451
454
|
|
|
452
455
|
function Canvas(w, h, opt) {
|
|
453
|
-
if (Q5._hasGlobal) return;
|
|
456
|
+
if (Q5._hasGlobal) return Promise.resolve(Q5.instances[0].canvas);
|
|
454
457
|
|
|
455
458
|
let useC2D = w == 'c2d' || h == 'c2d' || opt == 'c2d' || opt?.renderer == 'c2d' || !Q5._esm;
|
|
456
459
|
|
|
@@ -584,7 +587,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
584
587
|
if (!el) {
|
|
585
588
|
// reattach canvas to the DOM
|
|
586
589
|
document.getElementById(c.id)?.remove();
|
|
587
|
-
|
|
590
|
+
$._addCanvas();
|
|
588
591
|
}
|
|
589
592
|
|
|
590
593
|
if (window.IntersectionObserver) {
|
|
@@ -715,7 +718,7 @@ Q5.modules.canvas = ($, q) => {
|
|
|
715
718
|
}
|
|
716
719
|
};
|
|
717
720
|
|
|
718
|
-
|
|
721
|
+
$._addCanvas = () => {
|
|
719
722
|
let el = $._parent;
|
|
720
723
|
el ??= document.getElementsByTagName('main')[0];
|
|
721
724
|
if (!el) {
|
|
@@ -730,8 +733,8 @@ Q5.modules.canvas = ($, q) => {
|
|
|
730
733
|
if (document.body) document.body.appendChild(el);
|
|
731
734
|
});
|
|
732
735
|
}
|
|
733
|
-
}
|
|
734
|
-
|
|
736
|
+
};
|
|
737
|
+
$._addCanvas();
|
|
735
738
|
}
|
|
736
739
|
|
|
737
740
|
$.resizeCanvas = (w, h) => {
|
|
@@ -3456,16 +3459,12 @@ Q5.modules.fes = ($) => {
|
|
|
3456
3459
|
try {
|
|
3457
3460
|
let res = await (await fetch(fileUrl)).text(),
|
|
3458
3461
|
lines = res.split('\n'),
|
|
3459
|
-
errLine = lines[lineNum - 1]?.trim()
|
|
3460
|
-
bug = ['🐛', '🐞', '🐜', '🦗', '🦋', '🪲'][Math.floor(Math.random() * 6)],
|
|
3461
|
-
inIframe = window.self !== window.top,
|
|
3462
|
-
prefix = `q5.js ${bug}`,
|
|
3463
|
-
errorMsg = ` Error in ${fileBase} on line ${lineNum}:\n\n${errLine}`;
|
|
3462
|
+
errLine = lines[lineNum - 1]?.trim();
|
|
3464
3463
|
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3464
|
+
let type = '';
|
|
3465
|
+
if (e instanceof SyntaxError || e.name === 'SyntaxError') type = 'syntax';
|
|
3466
|
+
|
|
3467
|
+
Q5.friendlyError(fileBase, lineNum, errLine, type);
|
|
3469
3468
|
} catch (err) {}
|
|
3470
3469
|
};
|
|
3471
3470
|
|
|
@@ -3478,7 +3477,7 @@ Q5.modules.fes = ($) => {
|
|
|
3478
3477
|
let match = line.match(/(https?:\/\/[^\s)]+\.js|\b\/[^\s)]+\.js)/);
|
|
3479
3478
|
if (match) {
|
|
3480
3479
|
let file = match[1];
|
|
3481
|
-
if (!/q5|p5play/i.test(file)) {
|
|
3480
|
+
if (!/q5|p5play|q5play|brython/i.test(file)) {
|
|
3482
3481
|
$._sketchFile = file;
|
|
3483
3482
|
break;
|
|
3484
3483
|
}
|
|
@@ -3517,6 +3516,19 @@ Q5.modules.fes = ($) => {
|
|
|
3517
3516
|
checkLatestVersion();
|
|
3518
3517
|
}
|
|
3519
3518
|
};
|
|
3519
|
+
|
|
3520
|
+
Q5.friendlyError = (file, lineNum, detail) => {
|
|
3521
|
+
let bug = ['🐛', '🐞', '🐜', '🦗', '🦋', '🪲'][Math.floor(Math.random() * 6)],
|
|
3522
|
+
inIframe = window.self !== window.top,
|
|
3523
|
+
prefix = `q5 ${bug}`,
|
|
3524
|
+
msg = `Error in ${file} on line ${lineNum}`;
|
|
3525
|
+
|
|
3526
|
+
if (detail) msg += ':\n\n' + detail;
|
|
3527
|
+
|
|
3528
|
+
if (inIframe) return console.log(prefix + msg);
|
|
3529
|
+
|
|
3530
|
+
console.log(`%c${prefix}%c ${msg}`, 'background: #b7ebff; color: #000;', '');
|
|
3531
|
+
};
|
|
3520
3532
|
Q5.modules.input = ($, q) => {
|
|
3521
3533
|
if ($._isGraphics) return;
|
|
3522
3534
|
|
|
@@ -9349,17 +9361,17 @@ const userLangs = `
|
|
|
9349
9361
|
update -> es:actualizar
|
|
9350
9362
|
draw -> es:dibujar
|
|
9351
9363
|
postProcess -> es:postProcesar
|
|
9352
|
-
mousePressed -> es:
|
|
9353
|
-
mouseReleased -> es:
|
|
9354
|
-
mouseMoved -> es:
|
|
9355
|
-
mouseDragged -> es:
|
|
9364
|
+
mousePressed -> es:alPresionarRaton
|
|
9365
|
+
mouseReleased -> es:alSoltarRaton
|
|
9366
|
+
mouseMoved -> es:alMoverRaton
|
|
9367
|
+
mouseDragged -> es:alArrastrarRaton
|
|
9356
9368
|
doubleClicked -> es:dobleClic
|
|
9357
9369
|
keyPressed -> es:alPresionarTecla
|
|
9358
9370
|
keyReleased -> es:alSoltarTecla
|
|
9359
9371
|
touchStarted -> es:alEmpezarToque
|
|
9360
9372
|
touchEnded -> es:alTerminarToque
|
|
9361
9373
|
touchMoved -> es:alMoverToque
|
|
9362
|
-
mouseWheel -> es:
|
|
9374
|
+
mouseWheel -> es:ruedaRaton
|
|
9363
9375
|
`;
|
|
9364
9376
|
|
|
9365
9377
|
const classLangs = {
|
|
@@ -9440,20 +9452,19 @@ Object.defineProperty(Q5, 'lang', {
|
|
|
9440
9452
|
|
|
9441
9453
|
for (let className in classLangs) {
|
|
9442
9454
|
let target = className == 'Q5' ? Q5 : Q5[className] ? Q5[className].prototype : null;
|
|
9443
|
-
if (target)
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
}
|
|
9455
|
+
if (!target) continue;
|
|
9456
|
+
let map = parseLangs(classLangs[className], val);
|
|
9457
|
+
for (let name in map) {
|
|
9458
|
+
let translatedName = map[name];
|
|
9459
|
+
if (target.hasOwnProperty(translatedName)) continue;
|
|
9460
|
+
Object.defineProperty(target, translatedName, {
|
|
9461
|
+
get: function () {
|
|
9462
|
+
return this[name];
|
|
9463
|
+
},
|
|
9464
|
+
set: function (v) {
|
|
9465
|
+
this[name] = v;
|
|
9466
|
+
}
|
|
9467
|
+
});
|
|
9457
9468
|
}
|
|
9458
9469
|
}
|
|
9459
9470
|
|
|
@@ -9512,6 +9523,13 @@ Q5.addHook('init', (q) => {
|
|
|
9512
9523
|
for (let name in m) {
|
|
9513
9524
|
let translatedName = m[name];
|
|
9514
9525
|
q[translatedName] = q[name];
|
|
9526
|
+
|
|
9527
|
+
if (Q5._lang == 'es') {
|
|
9528
|
+
let unaccentedName = translatedName.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
9529
|
+
if (unaccentedName != translatedName) {
|
|
9530
|
+
q[unaccentedName] = q[name];
|
|
9531
|
+
}
|
|
9532
|
+
}
|
|
9515
9533
|
}
|
|
9516
9534
|
});
|
|
9517
9535
|
|
|
@@ -9524,6 +9542,8 @@ Q5.addHook('predraw', (q) => {
|
|
|
9524
9542
|
'frameCount',
|
|
9525
9543
|
'mouseX',
|
|
9526
9544
|
'mouseY',
|
|
9545
|
+
'pmouseX',
|
|
9546
|
+
'pmouseY',
|
|
9527
9547
|
'movedX',
|
|
9528
9548
|
'movedY',
|
|
9529
9549
|
'mouseIsPressed',
|
|
@@ -9536,7 +9556,14 @@ Q5.addHook('predraw', (q) => {
|
|
|
9536
9556
|
|
|
9537
9557
|
// sync properties
|
|
9538
9558
|
for (let p of props) {
|
|
9539
|
-
if (m[p])
|
|
9559
|
+
if (!m[p]) continue;
|
|
9560
|
+
q[m[p]] = q[p];
|
|
9561
|
+
if (Q5._lang == 'es') {
|
|
9562
|
+
let unaccentedName = m[p].normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
9563
|
+
if (unaccentedName != m[p]) {
|
|
9564
|
+
q[unaccentedName] = q[p];
|
|
9565
|
+
}
|
|
9566
|
+
}
|
|
9540
9567
|
}
|
|
9541
9568
|
});
|
|
9542
9569
|
const runPython = async function () {
|
|
@@ -9554,35 +9581,10 @@ const runPython = async function () {
|
|
|
9554
9581
|
document.head.appendChild(script);
|
|
9555
9582
|
});
|
|
9556
9583
|
|
|
9557
|
-
await loadScript('https://cdn.jsdelivr.net/npm/brython@3.
|
|
9558
|
-
await loadScript('https://cdn.jsdelivr.net/npm/brython@3.
|
|
9584
|
+
await loadScript('https://cdn.jsdelivr.net/npm/brython@3.14.0/brython.min.js');
|
|
9585
|
+
await loadScript('https://cdn.jsdelivr.net/npm/brython@3.14.0/brython_stdlib.min.js');
|
|
9559
9586
|
}
|
|
9560
9587
|
|
|
9561
|
-
brython();
|
|
9562
|
-
|
|
9563
|
-
__BRYTHON__.runPythonSource(`
|
|
9564
|
-
from browser import window, aio
|
|
9565
|
-
|
|
9566
|
-
async def runQ5PY(code, q5py):
|
|
9567
|
-
ns = globals().copy()
|
|
9568
|
-
ns['ns'] = ns
|
|
9569
|
-
ns['q5py'] = q5py
|
|
9570
|
-
|
|
9571
|
-
for attr in dir(q5py):
|
|
9572
|
-
if not attr.startswith('_'):
|
|
9573
|
-
try:
|
|
9574
|
-
ns[attr] = getattr(q5py, attr)
|
|
9575
|
-
except Exception:
|
|
9576
|
-
pass
|
|
9577
|
-
|
|
9578
|
-
exec(code, ns)
|
|
9579
|
-
|
|
9580
|
-
if "__run_code" in ns:
|
|
9581
|
-
await ns["__run_code"]()
|
|
9582
|
-
|
|
9583
|
-
window._runQ5PY = runQ5PY
|
|
9584
|
-
`);
|
|
9585
|
-
|
|
9586
9588
|
let code = '';
|
|
9587
9589
|
for (const script of scripts) {
|
|
9588
9590
|
code += script.src ? await (await fetch(script.src)).text() : script.innerText;
|
|
@@ -9591,30 +9593,111 @@ window._runQ5PY = runQ5PY
|
|
|
9591
9593
|
const useWebGPU = !code.slice(0, code.indexOf('\n')).includes('C2D'),
|
|
9592
9594
|
q5py = useWebGPU ? await Q5.WebGPU() : new Q5();
|
|
9593
9595
|
|
|
9596
|
+
// `window.Canvas` returns a promise that resolves when Q5 is ready
|
|
9597
|
+
// but `q5py.Canvas` returns the renderer synchronously
|
|
9598
|
+
// so to make Brython happy with `await Canvas()` we need to make it async
|
|
9599
|
+
const Canvas = q5py.Canvas;
|
|
9600
|
+
q5py.Canvas = async (...a) => Canvas(...a);
|
|
9601
|
+
|
|
9594
9602
|
code = code.replaceAll('\n', '\n\t');
|
|
9595
9603
|
|
|
9596
9604
|
code = `
|
|
9597
|
-
async def
|
|
9598
|
-
pass
|
|
9599
|
-
|
|
9605
|
+
async def __run():
|
|
9600
9606
|
${code}
|
|
9601
9607
|
|
|
9602
|
-
|
|
9608
|
+
_q5_state_vars = ["mouseX", "mouseY", "pmouseX", "pmouseY", "width", "height", "frameCount", "deltaTime", "mouseIsPressed", "mouseButton", "keyIsPressed", "key", "keyCode", "touches", "movedX", "movedY"]
|
|
9603
9609
|
|
|
9604
9610
|
def _sync_and_call(fn):
|
|
9605
9611
|
def _wrapper(*args):
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9612
|
+
try:
|
|
9613
|
+
for var in _q5_state_vars:
|
|
9614
|
+
if hasattr(q5py, var):
|
|
9615
|
+
ns[var] = getattr(q5py, var)
|
|
9616
|
+
return fn(*args)
|
|
9617
|
+
except Exception as e:
|
|
9618
|
+
window._pyErr(_err())
|
|
9619
|
+
raise e
|
|
9610
9620
|
return _wrapper
|
|
9611
9621
|
|
|
9612
|
-
for
|
|
9613
|
-
if
|
|
9614
|
-
setattr(window,
|
|
9622
|
+
for fn_name in ["update", "draw", "mousePressed", "mouseReleased", "mouseMoved", "mouseDragged", "mouseClicked", "doubleClicked", "mouseWheel", "keyPressed", "keyReleased", "keyTyped", "touchStarted", "touchMoved", "touchEnded", "windowResized"]:
|
|
9623
|
+
if fn_name in locals():
|
|
9624
|
+
setattr(window, fn_name, _sync_and_call(locals()[fn_name]))
|
|
9615
9625
|
`;
|
|
9616
9626
|
|
|
9617
|
-
|
|
9627
|
+
window._pyErr = (err, lineNum) => {
|
|
9628
|
+
if (typeof err === 'string' && err.includes('Traceback')) {
|
|
9629
|
+
let lines = err.split('\n');
|
|
9630
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9631
|
+
const match = lines[i].match(/File "<string>", line (\d+)/);
|
|
9632
|
+
if (match) {
|
|
9633
|
+
lineNum = parseInt(match[1]);
|
|
9634
|
+
lines = lines.slice(i + 1);
|
|
9635
|
+
// de-indent the first two lines based on the first line's indentation
|
|
9636
|
+
const indentMatch = lines[0].match(/^\s+/);
|
|
9637
|
+
if (indentMatch) {
|
|
9638
|
+
const indent = indentMatch[0];
|
|
9639
|
+
for (let j = 0; j < Math.min(2, lines.length); j++) {
|
|
9640
|
+
lines[j] = lines[j].slice(indent.length);
|
|
9641
|
+
}
|
|
9642
|
+
}
|
|
9643
|
+
err = lines.join('\n');
|
|
9644
|
+
break;
|
|
9645
|
+
}
|
|
9646
|
+
}
|
|
9647
|
+
}
|
|
9648
|
+
|
|
9649
|
+
let file = scripts[0].src.split('/').at(-1);
|
|
9650
|
+
lineNum -= 2; // adjust for the wrapper code lines
|
|
9651
|
+
if (Q5.friendlyError) Q5.friendlyError(file, lineNum, err);
|
|
9652
|
+
else console.error(`Error in ${file} on line ${lineNum}:\n\n${err}`);
|
|
9653
|
+
};
|
|
9654
|
+
|
|
9655
|
+
brython();
|
|
9656
|
+
|
|
9657
|
+
// hide brython's internal logs by temporarily overriding console.log
|
|
9658
|
+
let log = console.log;
|
|
9659
|
+
console.log = function () {};
|
|
9660
|
+
|
|
9661
|
+
__BRYTHON__.runPythonSource(`
|
|
9662
|
+
from browser import window, aio
|
|
9663
|
+
import traceback
|
|
9664
|
+
import io
|
|
9665
|
+
|
|
9666
|
+
def _err():
|
|
9667
|
+
f = io.StringIO()
|
|
9668
|
+
traceback.print_exc(file=f)
|
|
9669
|
+
return f.getvalue()
|
|
9670
|
+
|
|
9671
|
+
async def _run_py(q5py, code):
|
|
9672
|
+
ns = globals().copy()
|
|
9673
|
+
ns['ns'] = ns
|
|
9674
|
+
ns['q5py'] = q5py
|
|
9675
|
+
|
|
9676
|
+
for attr in dir(q5py):
|
|
9677
|
+
if not attr.startswith('_'):
|
|
9678
|
+
try:
|
|
9679
|
+
ns[attr] = getattr(q5py, attr)
|
|
9680
|
+
except Exception:
|
|
9681
|
+
pass
|
|
9682
|
+
|
|
9683
|
+
try:
|
|
9684
|
+
exec(code, ns)
|
|
9685
|
+
except SyntaxError as e:
|
|
9686
|
+
return window._pyErr(_err(), e.lineno)
|
|
9687
|
+
except Exception as e:
|
|
9688
|
+
return window._pyErr(_err())
|
|
9689
|
+
|
|
9690
|
+
try:
|
|
9691
|
+
await ns["__run"]()
|
|
9692
|
+
except Exception as e:
|
|
9693
|
+
window._pyErr(_err())
|
|
9694
|
+
|
|
9695
|
+
window._runPy = _run_py
|
|
9696
|
+
`);
|
|
9697
|
+
|
|
9698
|
+
console.log = log;
|
|
9699
|
+
|
|
9700
|
+
await window._runPy(q5py, code);
|
|
9618
9701
|
};
|
|
9619
9702
|
|
|
9620
9703
|
if (typeof document == 'object') {
|