Pytml

Pytml is a 15KB lightweight PyScript alternative — run Python in HTML with one script tag.

git clone https://github.com/notamitgamer/Pytml.git
· rebuilt hourlyOpen on GitHub
root / tests / runtime.test.js

runtime.test.js (3306B)


      1 import test from 'node:test';
      2 import assert from 'node:assert/strict';
      3 import { readFile } from 'node:fs/promises';
      4 
      5 const src = await readFile('src/pytml-runtime.js', 'utf8');
      6 
      7 
      8 test('Pytml pins Pyodide 0.314.0.7', () => {
      9   assert.match(src, /PYODIDE_VERSION = '0\.314\.0\.7'/);
     10   assert.match(src, /cdn\.jsdelivr\.net\/pyodide\/v\$\{PYODIDE_VERSION\}\/full/);
     11 });
     12 
     13 test('the requested fast boot preloads Pyodide before DOMContentLoaded', () => {
     14   assert.match(src, /preconnect\(\);/);
     15   assert.match(src, /preloadPyodideScript\(\);/);
     16   assert.match(src, /pyodide\.asm\.wasm/);
     17   assert.match(src, /script\.async = true/);
     18 });
     19 
     20 test('boot locks interactive controls while Python loads', () => {
     21   assert.match(src, /html\.pytml-booting button/);
     22   assert.match(src, /html\.pytml-booting input/);
     23   assert.match(src, /pytml-boot-pill/);
     24   assert.match(src, /Python failed to load\. Refresh to retry/);
     25 });
     26 
     27 test('numbered Python blocks and inline UI preprocessor are present', () => {
     28   assert.match(src, /const PY_BLOCK = \/\^py\(\?:\\d\+\)\?\$\/i;/);
     29   assert.match(src, /const PY_INLINE_UI = \/\^\(btn\|input\|txt\)\(\\d\+\)\$\/i;/);
     30   assert.match(src, /preprocessPythonBlock\(el\)/);
     31   assert.match(src, /createElement\(kind === 'btn' \? 'button'/);
     32   assert.match(src, /insertBefore\(fragment, el\)/);
     33   assert.match(src, /node\.remove\(\)/);
     34 });
     35 
     36 test('inline UI supports btn1 through btn99 and beyond', () => {
     37   const match = src.match(/const PY_INLINE_UI = (\/[^;]+;)/);
     38   assert.ok(match);
     39   assert.match(match[1], /\\d\+/);
     40 });
     41 
     42 test('the UI preprocessor does not use textContent as the source parser', () => {
     43   assert.match(src, /if \(child\.nodeType === Node\.TEXT_NODE\)/);
     44   assert.match(src, /code \+= child\.nodeValue \|\| ''/);
     45   assert.match(src, /code: processed\.code/);
     46 });
     47 
     48 test('normal DOM bindings, events, input(), joystick, and micropip remain available', () => {
     49   for (const token of [
     50     '_bind_all_dom', 'builtins.input = _pytml_input', 'addEventListener',
     51     'normalizeEvent', 'joystick', 'gamepads()', 'micropip.install'
     52   ]) assert.match(src, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
     53 });
     54 
     55 test('Python AST input transform compiles the AST directly', () => {
     56   assert.match(src, /ast\.fix_missing_locations\(tree\)/);
     57   assert.match(src, /compile\(tree, "<pytml>", "exec", flags=ast\.PyCF_ALLOW_TOP_LEVEL_AWAIT\)/);
     58   assert.doesNotMatch(src, /ast\.unparse\(tree\)/);
     59 });
     60 
     61 test('visible output is lazy and no persistent status blob is styled', () => {
     62   assert.match(src, /createOutputContainer\(\)/);
     63   assert.doesNotMatch(src, /\.pytml-status\{/);
     64   assert.doesNotMatch(src, /bottom:16px/);
     65 });
     66 
     67 test('calculator uses inline numbered buttons and not ordinary external calculator buttons', async () => {
     68   const html = await readFile('examples/calculator.html', 'utf8');
     69   assert.match(html, /<py1>/);
     70   assert.equal((html.match(/<btn\d+>/g) || []).length, 14);
     71   assert.doesNotMatch(html, /<button\b/);
     72   assert.match(html, /btn1\.on\("click"/);
     73 });
     74 
     75 test('demo gallery links to multiple runnable demos', async () => {
     76   const html = await readFile('demo.html', 'utf8');
     77   for (const page of ['calculator.html', 'counter.html', 'greeting.html', 'joystick.html', 'packages.html']) {
     78     assert.match(html, new RegExp(`examples/${page}`));
     79   }
     80 });