Skip to content

Enable lazy loading for CLI commands via cyclopts v4#20878

Draft
zzstoatzz wants to merge 2 commits intomainfrom
cli-lazy-loading
Draft

Enable lazy loading for CLI commands via cyclopts v4#20878
zzstoatzz wants to merge 2 commits intomainfrom
cli-lazy-loading

Conversation

@zzstoatzz
Copy link
Collaborator

Summary

  • Upgrade cyclopts from v3 to v4 and convert all 29 command registrations to lazy string-based imports (app.command("module:attr"))
  • Command modules are only imported when actually invoked, eliminating ~1s of unnecessary import overhead for targeted commands
  • Fix test that relied on implicit module availability via parent package attribute access

Performance

Before (all 25+ command modules imported eagerly at import prefect.cli._app):

import prefect.cli._app: ~3300ms
CLI modules loaded at import: 25+

After (zero command modules imported until needed):

import prefect.cli._app: ~1000ms
CLI modules loaded at import: 0
Resolving single command (e.g. 'config'): ~1ms

Note: prefect --help still imports all modules (cyclopts resolves all lazy commands to render the help page). The win is for prefect <specific-command> which now only imports the module it needs.

Test plan

  • All CLI tests pass (1152 passed, known-flaky start_server tests excluded)
  • All command aliases verified (flows, profiles, blocks, gcl, etc.)
  • Smoke-tested: --help, version, config view, profile ls, flow-run ls, server services ls, deploy --help
  • Verified zero command modules loaded at import time
  • Pre-commit hooks pass

🤖 Generated with Claude Code

Upgrade cyclopts from v3 to v4 and convert all 29 command
registrations in `_app.py` from eager imports to lazy string-based
registrations (`app.command("module:attr")`). Command modules are now
only imported when actually invoked, eliminating ~1s of unnecessary
import overhead for targeted commands like `prefect config view`.

- Bump cyclopts dependency from >=3.0 to >=4.0
- Replace 170+ lines of eager `from ... import` with 29 one-liner
  lazy registrations including explicit `name=` and `alias=`
- Add missing `import prefect.cli.dev` in test_dev.py (lazy loading
  means modules aren't available via attribute access on parent
  packages until explicitly imported)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codspeed-hq
Copy link

codspeed-hq bot commented Feb 26, 2026

Merging this PR will not alter performance

✅ 2 untouched benchmarks


Comparing cli-lazy-loading (d09cce7) with main (15781ee)

Open in CodSpeed

The `reset_sys_modules` autouse fixture deletes any modules added to
`sys.modules` during a test.  With lazy CLI loading, command modules
like `prefect.cli.events` are imported on-demand during test
invocations and then deleted after the test.  However, cyclopts
caches the resolved App internally (`CommandSpec._resolved`), so
subsequent tests get a stale App whose function references point to
the deleted module.  When monkeypatch patches the freshly re-imported
module, the stale cached App still uses the old (unpatched) functions.

Fix: skip `prefect.cli.*` modules during cleanup so cyclopts' cached
references remain valid.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant