Language Page

kcli for Python

This page is assembled from markdown in ktools-python. GitHub links point to the workspace repo and the relevant source files or directories.

6 markdown sections
QM-Code/ktools-python primary repo
HTML static output

Karma CLI Parsing SDK

ktools-python/kcli/README.md

kcli is the Python command-line parsing layer in the ktools ecosystem.

It supports two complementary CLI shapes:

  • top-level options such as --verbose
  • inline roots such as --build-*, --trace-*, or --config-*

The Python implementation follows the same core model as the C++ SDK:

  • Parser for top-level options, aliases, and positionals
  • InlineParser for one inline root such as --build
  • full-parse validation before any registered handler executes
  • parseOrExit() for executable-style handling and parseOrThrow() for tests and custom error handling

Documentation

Build And Test

Build from the ktools-python/ workspace root with the shared build tool:

python3 ../kbuild/kbuild.py --batch kcli --build-latest

If kbuild is already on your PATH, the equivalent command is:

kbuild --batch kcli --build-latest

From this repo root, run the Python tests directly:

python3 -m unittest discover -s tests
python3 -m unittest discover -s demo/tests

Demo entrypoints from this repo root:

python3 demo/bootstrap/main.py --verbose
python3 demo/exe/core/main.py --alpha-message hello
python3 demo/exe/omega/main.py --newgamma-tag prod
python3 demo/exe/omega/main.py --build

Behavior Highlights

  • parseOrExit() reports [error] [cli] ... to stderr and exits with code 2
  • parseOrThrow() raises kcli.CliError
  • bare inline roots such as --alpha or --build print inline help by default
  • root value handlers support bare-root values such as --trace '.*'
  • required-value handlers may consume a first value token that begins with -
  • literal -- is rejected as an unknown option; it is not treated as an option terminator

Demo Layout

  • demo/bootstrap/ minimal import and parse smoke test
  • demo/sdk/{alpha,beta,gamma} reusable inline parser modules
  • demo/exe/core/ local app handlers plus the alpha inline parser
  • demo/exe/omega/ combined alpha, beta, gamma, and local build parsers

Repository Layout

  • Public Python package: src/kcli/
  • Unit tests: tests/
  • Integration demos: demo/

The demos are part of the expected behavior contract and have subprocess-based coverage under demo/tests/.

Coding Agents

Read the workspace instructions first, then use the repo-local docs and tests as the implementation contract.

kcli Python

ktools-python/kcli/docs/index.md

kcli is a compact Python library for executable startup and command-line parsing. It is intentionally opinionated about normal CLI behavior:

  • parse first
  • fail early on invalid input
  • do not run handlers until the full command line validates
  • preserve the caller's argv
  • support grouped inline roots such as --trace-* and --config-*

Start Here

Typical Flow

import kcli


def on_verbose(context: kcli.HandlerContext) -> None:
    pass


def on_profile(context: kcli.HandlerContext, value: str) -> None:
    pass


parser = kcli.Parser()
build = kcli.InlineParser("--build")

build.setHandler("-profile", on_profile, "Set build profile.")

parser.addInlineParser(build)
parser.addAlias("-v", "--verbose")
parser.setHandler("--verbose", on_verbose, "Enable verbose logging.")

argv = ["tool", "--verbose", "--build-profile", "debug"]
parser.parseOrExit(len(argv), argv)

Core Concepts

Parser

  • owns top-level handlers, aliases, inline parser registrations, and the single parse pass

InlineParser

  • defines one inline root namespace such as --alpha, --trace, or --build

HandlerContext

  • exposes the effective option, command, root, and value tokens seen by the handler after alias expansion

CliError

  • used by parseOrThrow() to surface invalid CLI input and handler failures

Which Entry Point Should I Use?

Use parseOrExit() when:

  • you are in a normal executable startup path
  • invalid CLI input should print a standardized error and exit with code 2
  • you do not need custom formatting or recovery

Use parseOrThrow() when:

  • you want to customize error formatting
  • you want custom exit codes
  • you want to intercept and test parse failures directly

Build And Explore

python3 ../kbuild/kbuild.py --batch kcli --build-latest
python3 demo/exe/core/main.py --alpha-message "hello"
python3 demo/exe/omega/main.py --build
python3 -m unittest discover -s tests

Working References

If you want complete runnable examples, start with:

The public API contract lives in ../src/kcli.

API Guide

ktools-python/kcli/docs/api.md

This page summarizes the Python public API in src/kcli.

Core Types

Type Purpose
kcli.Parser Owns top-level handlers, aliases, positional handling, and inline parser registration.
kcli.InlineParser Defines an inline root such as --build plus its --build-* handlers.
kcli.HandlerContext Metadata passed to flag, value, and positional handlers.
kcli.CliError Raised by parseOrThrow() for invalid CLI input and handler failures.

HandlerContext

HandlerContext is passed to every registered handler.

Field Meaning
root Inline root name without leading dashes, such as build. Empty for top-level and positional handlers.
option Effective option token after alias expansion, such as --verbose or --build-profile.
command Normalized command name without leading dashes. Empty for positional dispatch and bare-root value handlers.
value_tokens Effective value tokens after alias preset expansion.

CliError

CliError(option, message) is raised by parseOrThrow() when:

  • the command line is invalid
  • a registered option handler raises
  • the positional handler raises

option() returns the option token associated with the failure when one is available.

InlineParser

Construction

build = kcli.InlineParser("--build")
build = kcli.InlineParser("build")

Both forms are accepted.

Root Control

build.setRoot("--newbuild")

This changes the inline root after construction.

Root Value Handler

build.setRootValueHandler(on_root)
build.setRootValueHandler(on_root, "<selector>", "Select build targets.")

Use this when the bare root should accept a value instead of only printing help.

Inline Handlers

build.setHandler("-flag", on_flag, "Enable build flag.")
build.setHandler("-profile", on_profile, "Set build profile.")
build.setOptionalValueHandler("-enable", on_enable, "Enable build mode.")

Inline options can be written in either form:

  • short inline form: -profile
  • fully-qualified form: --build-profile

Parser

Top-Level Handlers

parser.setHandler("--verbose", on_verbose, "Enable verbose logging.")
parser.setHandler("--output", on_output, "Set output target.")
parser.setOptionalValueHandler("--color", on_color, "Set or auto-detect color output.")

Top-level options may be provided as either:

  • "verbose"
  • "--verbose"

setHandler(...) inspects the callable arity:

  • one-argument handlers are treated as flags
  • two-argument handlers are treated as required-value handlers

Aliases

parser.addAlias("-v", "--verbose")
parser.addAlias("-c", "--config-load", ["user-file"])

Rules:

  • aliases use single-dash form such as -v
  • alias targets use double-dash form such as --verbose
  • preset tokens are prepended to the handler's effective value_tokens

Positional Handler

parser.setPositionalHandler(on_positionals)

The positional handler receives remaining non-option tokens in context.value_tokens.

Inline Parser Registration

parser.addInlineParser(build)

Duplicate inline roots are rejected.

Parse Entry Points

parser.parseOrExit(len(argv), argv)
parser.parseOrThrow(len(argv), argv)

parseOrExit()

  • preserves the caller's argv
  • prints errors to stderr
  • exits with code 2

parseOrThrow()

  • preserves the caller's argv
  • raises kcli.CliError
  • does not execute handlers until the parse validates

Parsing Behavior

ktools-python/kcli/docs/behavior.md

This page collects the Python kcli parsing rules that matter in practice.

Parse Lifecycle

kcli processes the command line in three stages:

  1. Copy the caller's argv into an internal token list.
  2. Validate the full command line and schedule handler invocations.
  3. Execute scheduled handlers only after validation succeeds.

This means:

  • handlers do not run on partially-valid command lines
  • unknown options fail the parse before any handler side effects occur
  • the caller's argv is preserved

Option Naming Rules

Top-level handlers:

  • accepted forms: "name" or "--name"
  • effective option token at runtime: --name

Inline roots:

  • accepted forms: "build" or "--build"
  • effective bare root token at runtime: --build

Inline handlers:

  • accepted forms: "-flag" or "--build-flag"
  • effective option token at runtime: --build-flag

Aliases:

  • alias form must be single-dash, such as -v
  • target form must be double-dash, such as --verbose

Inline Root Behavior

Bare inline roots behave specially.

--build

  • prints a help listing for the --build-* handlers

--build release

  • invokes the root value handler if one is registered
  • fails if no root value handler is registered

If a root value handler is registered with a placeholder and description, the bare-root help view includes a row such as:

--build <selector>  Select build targets.

Value Consumption Rules

Python kcli supports three public registration styles:

  • flag handlers consume no trailing value tokens
  • required-value handlers consume at least one value token
  • optional-value handlers consume values only when the next token looks like a value

Additional details:

  • once value collection starts, kcli keeps consuming subsequent non-option-like tokens for that handler
  • explicit empty tokens are preserved
  • joined handler values are produced by joining value_tokens with spaces

Examples:

--name "Joe"            -> value_tokens = ["Joe"]
--name "Joe" "Smith"    -> value_tokens = ["Joe", "Smith"]
--name ""               -> value_tokens = [""]
--profile -debug        -> value_tokens = ["-debug"]

Alias Behavior

Aliases are only expanded when a token is parsed as an option.

Examples:

parser.addAlias("-v", "--verbose")
parser.addAlias("-c", "--config-load", ["user-file"])

Rules:

  • consumed value tokens are not alias-expanded
  • preset tokens are prepended to effective value_tokens
  • preset tokens can satisfy required-value handlers
  • aliases with preset tokens cannot target flag handlers

Positionals

The positional handler receives all remaining non-option tokens in a single invocation.

Important details:

  • explicit empty positional tokens are preserved
  • positionals are dispatched only after option parsing succeeds

Failure Behavior

Unknown option-like tokens fail the parse.

Notable cases:

  • unknown top-level option: --bogus
  • unknown inline option: --build-unknown
  • literal --

Python kcli does not treat -- as an option terminator. It is reported as an unknown option.

Error Surface

parseOrExit()

  • prints [error] [cli] ... to stderr
  • exits with code 2

parseOrThrow()

  • raises kcli.CliError
  • preserves the human-facing error message
  • surfaces handler exceptions as CliError

Behavior Coverage

The Python behavior is covered by:

  • tests/test_kcli.py
  • demo/tests/test_core_cli.py
  • demo/tests/test_omega_cli.py

Examples

ktools-python/kcli/docs/examples.md

This page shows common Python kcli patterns. For complete runnable examples, also see:

Minimal Executable

import kcli


def on_verbose(context: kcli.HandlerContext) -> None:
    print(f"Processing {context.option}")


parser = kcli.Parser()
parser.addAlias("-v", "--verbose")
parser.setHandler("--verbose", on_verbose, "Enable verbose logging.")

Inline Root With Required And Optional Values

import kcli


def on_profile(context: kcli.HandlerContext, value: str) -> None:
    print(f"profile={value}")


def on_enable(context: kcli.HandlerContext, value: str) -> None:
    print(f"enable={value!r}")


build = kcli.InlineParser("--build")
build.setHandler("-profile", on_profile, "Set build profile.")
build.setOptionalValueHandler("-enable", on_enable, "Enable build mode.")

parser = kcli.Parser()
parser.addInlineParser(build)

This enables:

  • --build
  • --build-profile debug
  • --build-enable
  • --build-enable auto

Bare Root Value Handler

import kcli


def on_selector(context: kcli.HandlerContext, value: str) -> None:
    print(f"selector={value}")


trace = kcli.InlineParser("--trace")
trace.setRootValueHandler(on_selector, "<channels>", "Trace selected channels.")

This enables:

  • --trace
  • --trace '.app'
  • --trace '*.{net,io}'

Behavior:

  • --trace prints inline help
  • --trace '.app' invokes on_selector

Alias Preset Tokens

import kcli


def on_config_load(context: kcli.HandlerContext, value: str) -> None:
    print(context.option)
    print(context.value_tokens)
    print(value)


parser = kcli.Parser()
parser.addAlias("-c", "--config-load", ["user-file"])
parser.setHandler("--config-load", on_config_load, "Load config.")

This makes:

  • -c settings.json

behave like:

  • --config-load user-file settings.json

Inside the handler:

  • context.option is --config-load
  • context.value_tokens is ["user-file", "settings.json"]

Positionals

def on_positionals(context: kcli.HandlerContext) -> None:
    for token in context.value_tokens:
        use_positional(token)


parser.setPositionalHandler(on_positionals)

The positional handler receives all remaining non-option tokens after option parsing succeeds.

Custom Error Handling

If you want your own formatting or exit policy, use parseOrThrow():

try:
    parser.parseOrThrow(len(argv), argv)
except kcli.CliError as exc:
    print(f"custom cli error: {exc}")
    raise SystemExit(2)

Use this when:

  • you want custom error text
  • you want custom logging
  • you want a different exit code policy

Python Demos

ktools-python/kcli/demo/README.md

Python equivalents of the C++ demos under demo/.

Structure:

  • bootstrap/ minimal import and parse smoke test
  • sdk/ reusable inline parser modules
  • exe/ executable integration demos
  • tests/ subprocess-driven CLI checks for the Python demos

Demo pages:

Run examples from the repo root:

python3 demo/bootstrap/main.py --verbose
python3 demo/exe/core/main.py --alpha-message hello
python3 demo/exe/omega/main.py --newgamma-tag prod
python3 -m unittest discover -s demo/tests