Command-line API¶
eval_lcl retains value stdout; -o TREE selects the actual runtime tree.
parse_lcl renders static inspection, and -o EVAL captures the actual runtime
tree including ordinary failures. See text diagnostics for
cache leaves, inline error reasons and logger.traceback_style.
A console stream supplied through CliConfig.log_config is borrowed from the
application. Invocation cleanup does not close that stream, so accepted log
records can drain before the logger scope ends. File or override definitions
still take precedence over framework defaults.
Workflow variable defaults occupy a separate lowest-priority binding environment.
Command.default_bindings retains these declarations without evaluating them;
each invocation owns fresh factory caches. Existing entrance, preset, parameter,
configuration, and CLI precedence is unchanged. Help marks defaulted variables
optional, safely renders fixed values (including None), and displays factories
as <factory>. A dataclass field fallback also makes a workflow input optional
when all of its unresolved uses have a field fallback. These field fallbacks
apply only to their mapping locations and do not create LCL bindings.
lclang.cli is a typed async framework for Python scripts whose parameters may
come from declared defaults, .lclcfg files, and command-line overrides. A
decorated command handler receives one CliContext, including its invocation
Frame, as-of date, dry-run flag, and logging context, and returns a deterministic
CliResult.
CliEntrance(group, lcl_mixin={"service": service}) supplies shared host values
and callables to every command routed through the entrance, including commands
inside nested groups and workflows converted with to_cli(). The entrance
copies the mapping shallowly at definition time; the referenced objects remain
shared. Binding names are validated and trailing ! markers retain masking.
Each invocation combines these defaults with the selected command's host preset
before config expressions and logger settings are evaluated. Precedence is
entrance lcl_mixin, command/workflow preset, parameter defaults, config, then
CLI overrides, with later layers winning. Workflow lcl_mixin is part of its
command preset and therefore wins over entrance defaults. Host helpers do not
automatically become CLI parameters. Dynamic using targets keep the loader's
existing scope and do not receive host presets.
Original command definitions remain unchanged. Invocations own separate Frames
and caches, while supplied objects retain their ordinary shared Python state.
Running a command directly with command.run() does not apply entrance bindings.
The CLI resolves logging from the same final Frame as application configuration.
CliConfig.log_config supplies a LoggerHandlerConfig declaration; .lclcfg
settings and -o overrides replace the corresponding logger.* leaves.
logger.console controls the console and logger.file.<sink> declares named
files. logger.file.default supplies missing fields without creating a sink.
Explicit sink values always win over this template, regardless of which source
supplied the template. Thus a CLI default.enabled=False preserves a configured
sink's explicit enabled=True; override that sink directly to close it.
The logger scope starts after configuration is resolved and remains active
through command execution, workflow resources, and Frame cleanup. Preparation
errors go directly to stderr. Normal output drain completes before returning.
Overlapping CLI entry invocations are rejected because root logging is global.
All diagnostic console records use stderr; successful command results retain
stdout. The shared format uses local time with microseconds and a numeric offset
by default; -o logger.timezone utc selects UTC with a Z suffix. The timezone
setting also applies under --verbose. Verbose records display TRACE while
remaining DEBUG records. Their default prefix contains time, pid and thread;
other records retain source name, file/line and function. Explicit formats remain
effective. Each permanent file segment begins
with its absolute path. Execution audit records contain the banner, exact
redacted argv and lazy winning configuration without fixed initial positions.
--verbose enables runtime DEBUG diagnostics and lowers global and enabled
sink thresholds to DEBUG, retaining lower existing thresholds. Disabled sinks
stay disabled and source filters remain effective. Preparation records are not
replayed. Value rendering remains bounded and respects trailing-bang masks.
-v/--version retains its existing meaning.
Verbose records describe successful evaluations, input sources, cached values and shared results. A first failure includes a short reason, the actual execution phase and the request/owner context. Root requests summarize propagation and committed failure caches once. Cached failures reference the same failure identifier. The final exception diagnostic shows that identifier and retains complete source and value snapshots.
Each new evaluation has one completion event; its returned value is not logged again as another new-evaluation read. Successful roots add one dependency tree. Dynamic using targets emit one resolution event with the target path, declaration position and evaluation ID. Group reports share identical call prefixes and keep member order. Python callables include their implementation location when known.
Command execution and resource cleanup produce one final exception report on
stderr. File reports retain application and external-library Python stacks and
native causes by default; -o logger.traceback_style full includes lclang frames.
The traceback portion uses concise references for LCL exceptions whose full diagnostic was
already shown. A later output failure is reported separately and references an
earlier command failure when present. Successful command output remains stdout.
Logger expressions can use __as_of_date__, __dryrun__, __verbose__,
__ymd__, __execution_timestamp__, __command__, and __cli_params__.
The date and command metadata retain the existing runtime-key semantics.
Ordinary CLI values are strings; numeric, Boolean and collection overrides use
LCL[...]. No logger-specific coercion is added. Unknown logger keys are errors,
including in Workflow commands where valid logger keys bypass business-input
validation. See the logger reference for the complete sink contract.
Built-in commands use this same routing model. Exact valueless -o FORCE makes
a marked RESULT=LCL[...] parse strictly; ordinary override parsing remains
permissive. Invalid marked logger overrides fail when their effective field is
validated.
Dry-run remains an explicit handler decision, so the framework never pretends to know whether an application-specific side effect is safe.
Parameter help¶
Command help groups documented parameters by their full parent scope, with
unqualified names in a first (global) block. Scopes and rows sort alphabetically
(ignoring case), and rows retain full names for copying into -o overrides.
Type annotations omit the collections.abc. prefix, including nested types.
Static defaults make an input optional and appear as default=.... Help uses
the same precedence as execution: entrance bindings, command/workflow presets,
then declared parameter defaults. A preset entry containing None, False,
zero, or an empty container is still a default. ParameterDoc.default=None
retains its existing meaning of no declared default. Defaults use bounded,
single-line representations; masked values appear as *masked*.
Help never loads the selected -c file, evaluates configuration expressions,
initializes logging, or runs the command. Extra host helpers are not parameters.
Trusted command discovery¶
lclang.cli.scan_commands(module, name, description) imports the developer's
own trusted Python package and submodules in deterministic order. It collects
public module-level Commands, deduplicates re-exports by identity and rejects
distinct commands sharing a name. Importing developer-owned code is the intended
extension mechanism; this API is not a scanner for hostile or untrusted modules.
See the CLI tutorial for a progressive application and workflow reference for conversion.
See errors and diagnostic codes for exception fields, specific
codes, cause chains, and executable troubleshooting examples. Ordinary library
validation uses LclValidationError; native callback and operation failures
retain their original exception in __cause__.
The exception guide describes each failure family, grouped
execution and cleanup failures, actual diagnostic output, and application
handling with except* and native control signals.
Native controls, including iterator termination and groups that contain it, propagate after owned Frames close. Earlier ordinary failures remain in their cause chain. Logger configuration or startup failures also retain invocation cleanup failures in a combined diagnostic rather than hiding them during final cleanup.