Lazy loading defers the import of command handler modules until the command is actually invoked. This keeps CLI startup fast even with dozens of commands that have heavy dependencies.
lazy_command
Register a command with a dotted import path instead of a function reference:
from milo import CLI
cli = CLI(name="myapp")
cli.lazy_command(
"deploy",
"myapp.commands.deploy:deploy_handler",
description="Deploy to an environment",
)
The module myapp.commands.deploy is not imported until someone runs myapp deploy. All other commands start instantly.
Pre-computed schemas
By default, the schema is generated by importing the handler and inspecting its signature. To avoid even that import, provide the schema upfront:
cli.lazy_command(
"deploy",
"myapp.commands.deploy:deploy_handler",
description="Deploy to an environment",
schema={
"type": "object",
"properties": {
"target": {"type": "string"},
"dry_run": {"type": "boolean"},
},
"required": ["target"],
},
)
With a pre-computed schema, --llms-txt and --mcp tools/listwork without importing any handler modules.
Lazy commands in groups
Groups support lazy loading too:
site = cli.group("site", description="Site operations")
site.lazy_command(
"build",
"myapp.commands.site:build_handler",
description="Build the site",
)
How it works
LazyCommandDefstores the import path and defers resolution:
- On registration, only the name, description, and optional schema are stored
- On first invocation,
resolve()imports the module and extracts the handler - The result is cached as a full
CommandDef— subsequent calls skip the import
Resolution is thread-safe (uses a lock with double-check pattern).
When to use lazy loading
- CLIs with many commands where only one runs per invocation
- Commands that import heavy dependencies (cloud SDKs, ML libraries)
- Plugin systems where third-party command modules may not be installed
For small CLIs with lightweight imports, the@cli.commanddecorator is simpler and equally fast.
Tip
Combine pre-computed schemas with--llms-txt and --mcpto let AI agents discover all your commands without triggering any imports.