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
commit be64e02088ec159186aafe0a22fb70a255599db3
parent aca3d3a32b394ac240c409e351a4d46f4cc710d6
Author: ASWANTH R <aswanthr82@gmail.com>
Date:   Mon, 19 Oct 2026 16:56:11 +0530

Update Pytml to version 2.5.1 with improvements (#15)
Diffstat:
Mpytml.js | 42+++++++++++++++++++++++++++++++-----------
1 file changed, 31 insertions(+), 11 deletions(-)

diff --git a/pytml.js b/pytml.js @@ -6,7 +6,7 @@ fetch('https://pytml.vercel.app/api/count', { .then(data => console.log('Pytml loaded:', data.message, 'times')) .catch(() => {}); /* - * Pytml 2.5.0 + * Pytml 2.5.1 * Python + HTML, powered by Pyodide. * * Pytml 2.5 includes a lightweight inline UI preprocessor for <pyN> blocks. @@ -15,7 +15,7 @@ fetch('https://pytml.vercel.app/api/count', { (function (global) { 'use strict'; - const VERSION = '2.5.0'; + const VERSION = '2.5.1'; const PYODIDE_VERSION = '314.0.7'; const PYODIDE_INDEX = `https://cdn.jsdelivr.net/pyodide/v${PYODIDE_VERSION}/full/`; const PYODIDE_SCRIPT = `${PYODIDE_INDEX}pyodide.js`; @@ -338,8 +338,12 @@ def query(selector): return element(selector) def query_all(selector): return [Element(x) for x in js.document.querySelectorAll(str(selector))] def joystick(target): return Joystick(js.pytmlInstance.makeJoystick(target).element) async def sleep(ms): await js.pytmlInstance.sleep(int(ms)) -def timeout(callback, ms): return js.pytmlInstance.setTimeout(callback, int(ms)) -def interval(callback, ms): return js.pytmlInstance.setInterval(callback, int(ms)) +def timeout(callback, ms): + from pyodide.ffi import create_proxy + return js.pytmlInstance.setTimeout(create_proxy(callback), int(ms)) +def interval(callback, ms): + from pyodide.ffi import create_proxy + return js.pytmlInstance.setInterval(create_proxy(callback), int(ms)) def clear_timeout(timer_id): return js.pytmlInstance.clearTimeout(int(timer_id)) def clear_interval(timer_id): return js.pytmlInstance.clearInterval(int(timer_id)) async def clipboard_copy(text): await js.pytmlInstance.clipboard_write(str(text)) @@ -355,8 +359,8 @@ def install(requirements, **kwargs): import micropip return micropip.install(requirements, **kwargs) -version = '2.5.0' -pyodide_version = '0.314.0.7' +version = '2.5.1' +pyodide_version = '314.0.7' def _bind_all_dom(namespace): elements = {} @@ -461,8 +465,8 @@ def _bind_all_dom(namespace): this.readyPromise = null; this.programs = new Map(); this._callbackRegistry = new Map(); - this._intervals = new Set(); - this._timeouts = new Set(); + this._intervals = new Map(); + this._timeouts = new Map(); this._joystickState = new WeakMap(); } @@ -815,18 +819,34 @@ async def _pytml_run_source(source): const id = window.setTimeout(() => { try { fn(); } catch (e) { this.addError(`Timer error: ${e.message}`); } }, Number(ms)||0); - this._timeouts.add(id); + this._timeouts.set(id, fn); return id; } - + setInterval(fn, ms) { const id = window.setInterval(() => { try { fn(); } catch (e) { this.addError(`Timer error: ${e.message}`); } }, Number(ms)||0); - this._intervals.add(id); + this._intervals.set(id, fn); return id; } + clearTimeout(id) { + window.clearTimeout(id); + const fn = this._timeouts.get(id); + if (fn) { + try { fn.destroy(); } catch (_) {} + this._timeouts.delete(id); + } + } + clearInterval(id) { + window.clearInterval(id); + const fn = this._intervals.get(id); + if (fn) { + try { fn.destroy(); } catch (_) {} + this._intervals.delete(id); + } + } clipboard_write(text) { if (!navigator.clipboard?.writeText) return Promise.reject(new Error('Clipboard API unavailable')); return navigator.clipboard.writeText(String(text));