Module lifecycle

koskari.module

Module lifecycle for Koskari source files.

Provides new_module, init_module, load_module_async, and load_module_demand. new_module is synchronous and copies parent bindings from koskari.builtins. Module body loading may use the async or demand execution path.

Each top-level expression is read, compiled to a Code object, executed as a koskari expression coroutine (async path) or via demand_call (demand path), and run in sequence. This allows each expression to mutate the module namespace for subsequent expressions, e.g. by adding new bindings or special forms.

This is the koskari analogue of sibilant.module from python-sibilant.

koskari.module.attach_module_load_facets(module, pragma, compiler, reader)

Bind __pragma__, __compiler__, and __reader__ on module for load time.

Parameters:
  • pragma – ModulePragma instance for this load

  • module – koskari Module (or object whose vars() is the ns)

  • compiler – Compiler instance for this load

  • reader – LayoutReader instance for this load

koskari.module.build_module_load_facets(module, filename=None)

Produce the load-time pragma, compiler, and reader for module.

When all three facets are already bound on the module (a prior load ran with detach=False), they are reused as-is rather than rebuilt, so consecutive loads share one pragma lineage, one compiler (and its open code allocator), and one reader. The compiler’s filename is refreshed so code minted by this load attributes backtrace positions to the current source file.

Otherwise the module’s bound __pragma__ is kept when present (a detached load leaves it as the module’s record), or a ModulePragma is built and bound as __pragma__; the compiler and reader are then chained off it via from_pragma (each subscribing for later pragma changes) and bound as load facets. A fresh pragma comes from build_module_pragma(), which seeds it from the nearest ancestor package’s __package_pragma__.

Parameters:
  • module – koskari Module (or object whose vars() is the ns)

  • filename – optional source filename

Returns:

the (compiler, reader) pair

koskari.module.build_module_pragma(module)

The module’s __pragma__ record, bound if it was not already.

A bound __pragma__ is returned as-is. Otherwise this is the single chokepoint where the pragma source is resolved: the nearest ancestor package’s __package_pragma__ seeds the settings when one exists (a copy with a fresh watcher set – watchers never cross modules), falling back to the built-in default. A package additionally pulls the inherited default down as its own __package_pragma__ so its descendants inherit it; a (package-pragma ...) declaration in the package body overwrites that stamp.

Parameters:

module – koskari Module (or object whose vars() is the ns)

Returns:

the ModulePragma bound as __pragma__

koskari.module.compile_top_level(compiler, reader, source_stream)

Read and compile each top-level form of a module’s source in order, yielding one expression Code per form. The caller runs each before the next is read, so compile-time artifacts a form defines (syntax, reader macros) are live when the following form compiles.

A leading string is the module docstring: it is rewritten to a var of __doc__ and yielded like any other form, which is also the body a .kio carries for it

Parameters:
  • compiler – Compiler facet for this load

  • reader – LayoutReader facet for this load

  • source_stream – SourceStream from source_open or source_str

koskari.module.detach_module_load_facets(module)

Remove load-time __compiler__ and __reader__ bindings.

__pragma__ stays bound as the module’s record; the get-compiler runtime function rebuilds a compiler from it on demand. Ignores names listed in MODULE_DETACH_FACET_NAMES that are not bound.

Parameters:

module – koskari Module (or object whose vars() is the ns)

koskari.module.exports_materialized(module)

Materialize __all__ when the block ends, whether or not it raised

koskari.module.find_module_pragma(ns)

The pragma record for the module owning ns. Returns the bound __pragma__, or seeds one from the nearest package pragma, as a load would, and binds it

Parameters:

ns – a module, function, or let namespace, or a plain mapping

Returns:

the ModulePragma

koskari.module.find_module_reader(ns)

The layout reader for the module owning ns. Returns the bound __reader__, or rebuilds one from the module’s pragma and binds it when the load-time facet was detached

Parameters:

ns – a module, function, or let namespace, or a plain mapping

Returns:

the LayoutReader

koskari.module.init_module(module, filename, spec=None)

Initialise a koskari Module with source file metadata.

Sets __file__, and if the filename is an __init__.ki also sets __path__ to the containing directory. Stores __spec__ and __loader__ when provided via spec.

Parameters:
  • module – koskari Module instance

  • filename – absolute path to the .ki source file

  • spec – importlib ModuleSpec, or None

koskari.module.kio_codes(module)

The stored bodies of a module’s precompiled .kio file, with the module’s __pragma__ record bound first so (pragma ...) bodies and package-pragma stamping run as they do under a source load. No compiler or reader is built: the bodies were compiled already. Every function the file holds is decoded before any body runs, so the function code its build placed in arena chunks is placed and sealed here, before the first body

Parameters:

module – koskari Module with __file__ set to a .kio path

Returns:

the Code objects in stored order

koskari.module.load_facets(module, filename, *, seal, detach)

The facets of one load. Builds (or reuses) the compiler and reader, opens the compiler’s code allocator so the load’s function code is placed into arena chunks – unless the load can never seal, with seal False and detach set, where no allocator is opened: a fresh compiler then builds function code on the heap, while a compiler reused from an earlier detach False load keeps its open allocator and still places into it – and yields them; when the block ends, whether or not the load raised, seals the chunks when seal is set, and when detach is set materializes __all__ and detaches the facets, with __pragma__ staying bound. Callers passing detach False own the completion steps: an interactive session seals but retains facets; the bootstrap builtins loop defers both across its source files and performs them once at the end

Parameters:
  • module – koskari Module (or object whose vars() is the ns)

  • filename – source filename for the compiler

  • seal – on success, seal the arena chunks the load placed its function code into

  • detach – materialize exports and drop the load facets

Returns:

the (compiler, reader) pair

async koskari.module.load_module_async(module, *, seal=True, detach=True)

Execute a koskari module by reading, compiling, and running each top-level expression in order.

Each expression is compiled relative to the module, then executed as an expression coroutine and awaited. Bindings created by one expression are visible to all subsequent expressions, supporting define, defsyntax, and reader macro registration at module load time.

While loading, __pragma__, __compiler__, and __reader__ are bound on the module; facets already present (a prior load with detach=False) are reused, so consecutive loads share one pragma, compiler, and reader. Sealing and detaching follow load_facets().

Parameters:
  • module – koskari Module instance with __file__ set

  • seal – when True, seal the module on successful completion

  • detach – when True, detach the compiler and reader facets afterward

koskari.module.load_module_demand(module, *, seal=True, detach=True)

Execute a koskari module by reading, compiling, and running each top-level expression in order via demand_call.

Same structure as load_module_async() but each expression is run with demand_call instead of awaiting a coroutine. No async suspension is allowed. Used by the loader’s exec_module_demand path.

Parameters:
  • module – koskari Module instance with __file__ set

  • seal – when True, seal the module on successful completion

  • detach – when True, detach the compiler and reader facets afterward

async koskari.module.load_module_kio_async(module)

Execute a module from a precompiled .kio file.

Each stored body is re-hardened and run as a top-level expression in order, matching the incremental structure of load_module_async; __all__ is materialized from declare-exports at the end.

Parameters:

module – koskari Module with __file__ pointing at a .kio path

koskari.module.load_module_kio_demand(module)

Execute a module from a precompiled .kio file via demand_call.

Same steps as load_module_kio_async but synchronous.

Parameters:

module – koskari Module with __file__ set to a .kio path

async koskari.module.load_module_source_async(module, source, *, seal=True, detach=True, layout=True)

Execute koskari source passed as a string.

Same top-level expression loop as load_module_async(), but reads from source instead of opening module’s __file__.

Parameters:
  • module – koskari Module instance with __file__ set

  • source – koskari source text to execute

  • seal – when True, seal the module on successful completion

  • detach – when True, detach the compiler and reader facets afterward

  • layout – when False, read plain s-expressions instead of layout, by turning reader/layout off on the module pragma for this load, so a pragma form in the source keeps it off; the prior value is restored afterward. A layout line is a single form, so several expressions on one line become one implicit call; that suits a file but not a one-liner, where consecutive top-level forms are wanted

koskari.module.new_module(name, package=None, system=False)

Create a new koskari Module with an initialised ModuleNamespace

Parent bindings are copied from the loaded koskari.builtins Python module (bootstrap registry plus basics.ki). No event loop or await is required.

Parameters:
  • name – fully-qualified module name string

  • package – parent package name string, or None

  • system – when True, register the module in sys.modules

Returns:

koskari Module instance

async koskari.module.run_codes_async(module, codes)

Run each Code as an expression of module, awaiting each in turn.

Each expression is dropped before the next form is read, so the function code only it holds dies while its chunk is open and the next function takes its bytes; a helper so no caller local holds the last one past the loop either, before the compiler seals

koskari.module.run_codes_demand(module, codes)

Run each Code as an expression of module via demand_call. No async suspension is allowed. See run_codes_async() for why the loop lives in a helper