Hosting Koskari from Python
Run Koskari from inside a Python program: import .ki modules, execute
a file into a namespace you hold, or open a REPL, and call back into
Koskari from Python.
Importing a module
import koskari installs the import hook, after which any .ki file on
sys.path imports like a Python module:
import koskari # installs the .ki import hook
import mypkg.tools # loads mypkg/tools.ki
result = mypkg.tools.SOME_CONSTANT
This covers .ki code packaged beside Python. No event loop is needed;
imports run on the demand path. See
Demand, async, and defer.
Running a file into a namespace
To execute one .ki file into a module object you hold, without the
REPL, use koskari.module:
import asyncio
import koskari
from koskari.module import (new_module, init_module,
load_module_async)
async def main():
mod = new_module('__app__') # a fresh Koskari module
init_module(mod, 'app.ki') # attach the file and the specials
await load_module_async(mod) # run the top-level forms
greeting = await mod.greet('world') # a Koskari function is awaited
print(greeting)
asyncio.run(main())
init_module followed by load_module_async is what the CLI’s
run_file_async does; calling them directly avoids the REPL dependency.
The REPL and the CLI wrappers
koskari.cli.host wraps the two steps above in one call,
run_file_async, which is what the koskari command runs:
import asyncio
from koskari.cli.host import run_file_async
from koskari.module import new_module
async def main():
mod = new_module('__main__')
await run_file_async(mod, 'app.ki') # init_module plus load
print(await mod.greet('world'))
asyncio.run(main())
The interactive loop is repl, a Koskari function in
koskari.cli.repl. It pulls in prompt-toolkit, so import it only where
an interactive session is wanted; awaiting it on a loaded module hands
that module’s namespace to the prompt:
from koskari.cli.repl import repl
await repl(mod) # blocks until the user exits
A headless service takes the koskari.module path above and leaves
prompt-toolkit out.
Calling into Koskari
A Koskari function called from Python returns an awaitable, not a value, because every Koskari function can suspend. From an async context, await it:
greeting = await mod.greet('world') # 'hello, world'
A function defined with @demand above its def returns the
value directly and can be called with no event loop. Outside a loop, or
when handing a result to a raw asyncio API, bridge it:
from koskari.asyncio import as_coroutine
coro = as_coroutine(mod.greet('world')) # a Python coroutine
Which construct fits which situation is the decision table.
Summary
Need |
Use |
|---|---|
|
|
importing modules |
|
running a file headless |
|
the CLI’s file-running behaviour |
|
an interactive prompt |
|
calling a Koskari function |
|
no event loop |
the demand path; a suspension raises |
See also
Interop: calls across the boundary
Demand, async, and defer: the host model
Packaging an installed command: a
.kiprogram as a pip command