Application components
Components are mixins (or ready-made parser classes) you attach to your app.
They add arguments and participate in the parser lifecycle.
Use them when you need the feature — Clak’s core stays small without them.
| Component |
What you get |
Guide |
| Views |
Tables / JSON / CSV / YAML from cli_run return values |
Views |
| Logging |
-v tiers, formatters, self.logger |
Logging |
| Config |
XDG paths + load --conf-file |
Config |
| Completion |
Emit shell completion scripts |
Completion |
API pages: logging,
views,
config,
completion.
Views
Render command results as tables (or pretty-prints) with a single mixin.
| script_views.py |
|---|
| #!/usr/bin/env python3
"""Demo: list users with ListViewMixin and CLI column control."""
from clak import Argument, ListViewMixin, Parser
class AppMain(ListViewMixin, Parser):
"""List demo users as a table.
Try:
./script_views.py
./script_views.py --columns name,role
./script_views.py --add-index
./script_views.py --columns name --no-expand-keys
./script_views.py --format json --columns name,role
./script_views.py --sort-columns name --sort-mode desc
"""
class Meta:
# True = all flags, False = none, or a subset like ("columns",)
view_cli_options = True
role = Argument("--role", help="Filter by role")
def cli_run(self, role=None, **_):
users = [
{"name": "ada", "role": "admin", "city": "London"},
{"name": "linus", "role": "dev", "city": "Helsinki"},
{"name": "grace", "role": "dev", "city": "New York"},
]
if role:
users = [user for user in users if user["role"] == role]
return users
if __name__ == "__main__":
AppMain()
|
Logging
Configure -v tiers, formatters, and self.logger.
| script_logging.py |
|---|
| #!/usr/bin/env python3
"""Demo: structured CLI logging with LoggingOptMixin.
Try:
./script_logging.py
./script_logging.py -v
./script_logging.py -vv
./script_logging.py -vvv
./script_logging.py -vv --log-format extended
./script_logging.py greet Ada
"""
from __future__ import annotations
import logging
from clak import Argument, Command, LoggingOptMixin, Parser
logger = logging.getLogger(__name__)
class GreetCmd(Parser):
"""Greet someone and emit logs at several levels."""
name = Argument("NAME", help="Name to greet", default="World", nargs="?")
def cli_run(self, name="World", **_):
self.logger.debug("Preparing greeting for %s", name)
self.logger.info("Hello, %s", name)
self.logger.success("Greeting delivered")
self.logger.notice("Tip: pass -v / -vv for more detail")
print(f"Hello, {name}!")
class AppMain(LoggingOptMixin, Parser):
"""Logging demo.
``Meta.log_levels`` lists cumulative ``-v`` tiers as ``LEVEL|logger``.
An empty logger name configures the root logger.
"""
class Meta:
log_prefix = __name__
log_default_level = "WARNING"
log_levels = [
["WARNING|" + __name__], # default
["INFO|" + __name__], # -v
["DEBUG|" + __name__], # -vv
["DEBUG|"], # -vvv (root — includes clak internals)
]
# Kept at WARNING until maximum verbosity (-vvv here)
log_silent = ["urllib3", "asyncio"]
greet = Command(GreetCmd)
def cli_run(self, **_):
logger.warning("App module logger (logging.getLogger(__name__))")
self.logger.info("Parser logger (self.logger) — visible from -v")
self.logger.debug("Debug line — visible from -vv")
print("Run a subcommand, e.g. greet — or pass -h")
if __name__ == "__main__":
AppMain()
|
Config
Expose XDG paths and load a JSON/YAML config file into ctx.config:
from clak import Parser, XDGConfigMixin
class App(XDGConfigMixin, Parser):
class Meta:
app_name = "myapp"
def cli_run(self, ctx, **_):
print(ctx.config)
Details: Config.
Completion
Add a completion subcommand that prints an argcomplete shell script:
from clak import CompCmdRender, Command, Parser
class App(Parser):
completion = Command(CompCmdRender, help="Print shell completion script")
eval "$(python myapp.py completion --executable myapp --shell bash)"
Details: Completion.
Next steps