Python › Command-Line Interfaces (argparse)

How do you build a plugin-style CLI where subcommands are dynamically discovered and registered from separate modules, rather than hardcoded?

Advanced

Answer

Use Python's entry_points mechanism (declared in pyproject.toml) or a simple plugin-discovery pattern (scanning a 'commands' package/directory) to dynamically import each subcommand module and call a shared 'register(subparsers)' function it exposes — letting new subcommands be added by simply dropping in a new file, without modifying the main CLI script.
# commands/deploy.py
def register(subparsers):
    parser = subparsers.add_parser("deploy")
    parser.add_argument("--env", required=True)
    parser.set_defaults(func=lambda args: deploy(args.env))

# main.py
for module in discover_command_modules():
    module.register(subparsers)
args = parser.parse_args()
args.func(args)  # dispatches to the registered handler
Real-world example Building an extensible CLI tool (like a plugin-based deployment tool) where new subcommands can be added without touching the core script.

Common follow-ups: How does 'set_defaults(func=...)' provide a clean way to dispatch parsed arguments to the correct handler function?