Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/timer.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand Down
34 changes: 28 additions & 6 deletions klongpy/sys_fn_timer.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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):
Expand Down
24 changes: 24 additions & 0 deletions tests/test_sys_fn_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()