diff --git a/docs/timer.md b/docs/timer.md index e4418ac..6dc6067 100644 --- a/docs/timer.md +++ b/docs/timer.md @@ -7,6 +7,10 @@ cb::{.p("hello")} th::.timer("greeting";1;cb) ``` +Timers look up the callback function each time they execute. Updating the +function definition after creating the timer will change the behavior on the +next tick. + To stop the timer, it can be closed via: ``` diff --git a/klongpy/sys_fn_timer.py b/klongpy/sys_fn_timer.py index ec106a8..0e3c6cc 100644 --- a/klongpy/sys_fn_timer.py +++ b/klongpy/sys_fn_timer.py @@ -1,7 +1,7 @@ import asyncio import sys -from klongpy.core import KGCall, KGFn, KGFnWrapper +from klongpy.core import KGSym, KGCall, KGFn, KGFnWrapper class KGTimerHandler: @@ -73,15 +73,37 @@ def eval_sys_fn_timer(klong, x, y, z): th::.timer("count";1;cb) """ - y= int(y) + y = int(y) if y < 0: return "x must be a non-negative integer" - z = z if isinstance(z, KGCall) else KGFnWrapper(klong, z) if isinstance(z, KGFn) else z - if not callable(z): - return "z must be a function" + + sym = None + if isinstance(z, (KGFn, KGCall)): + for ctx in reversed(klong._context._context): + for k, v in ctx.items(): + if v is z and k not in (KGSym('x'), KGSym('y'), KGSym('z')): + sym = k + break + if sym is not None: + break + elif isinstance(z, KGSym): + sym = z + + if sym is not None: + def callback(): + fn = klong[sym] + fn = fn if isinstance(fn, KGCall) else KGFnWrapper(klong, fn) if isinstance(fn, KGFn) else fn + if not callable(fn): + raise RuntimeError("z must be a function") + return fn() + else: + callback = z if isinstance(z, KGCall) else KGFnWrapper(klong, z) if isinstance(z, KGFn) else z + if not callable(callback): + return "z must be a function" + system = klong['.system'] klongloop = system['klongloop'] - return _call_periodic(klongloop, x, y, z) + return _call_periodic(klongloop, x, y, callback) def eval_sys_fn_cancel_timer(x): diff --git a/tests/test_sys_fn_timer.py b/tests/test_sys_fn_timer.py index 785ef68..e3ef062 100644 --- a/tests/test_sys_fn_timer.py +++ b/tests/test_sys_fn_timer.py @@ -131,3 +131,27 @@ async def _test_result(): task = self.klongloop.call_soon_threadsafe(asyncio.create_task, _test()) asyncio.run_coroutine_threadsafe(_test_result(), self.klongloop).result() task.cancel() + + def test_timer_dynamic_lookup(self): + klong = KlongInterpreter() + klong['.system'] = {'ioloop': self.ioloop, 'klongloop': self.klongloop} + + async def _test(): + klong('result::""') + klong('cb::{result::"h1";1}') + klong('th::.timer("test";0;cb)') + while klong('result') != 'h1': + await asyncio.sleep(0) + klong('cb::{result::"h2";0}') + + async def _test_result(): + r = klong('result') + while r != 'h2': + await asyncio.sleep(0) + r = klong('result') + r = klong('.timerc(th)') + self.assertEqual(r,0) + + task = self.klongloop.call_soon_threadsafe(asyncio.create_task, _test()) + asyncio.run_coroutine_threadsafe(_test_result(), self.klongloop).result() + task.cancel()