I have one large click application that I've developed, but navigating through the different commands/subcommands is getting rough. How do I organize my commands into separate files? Is it possible to organize commands and their subcommands into separate classes?
Here's an example of how I would like to separate it:
init
import click
@click.group()
@click.version_option()
def cli():
pass #Entry Point
command_cloudflare.py
@cli.group()
@click.pass_context
def cloudflare(ctx):
pass
@cloudflare.group('zone')
def cloudflare_zone():
pass
@cloudflare_zone.command('add')
@click.option('--jumpstart', '-j', default=True)
@click.option('--organization', '-o', default='')
@click.argument('url')
@click.pass_obj
@__cf_error_handler
def cloudflare_zone_add(ctx, url, jumpstart, organization):
pass
@cloudflare.group('record')
def cloudflare_record():
pass
@cloudflare_record.command('add')
@click.option('--ttl', '-t')
@click.argument('domain')
@click.argument('name')
@click.argument('type')
@click.argument('content')
@click.pass_obj
@__cf_error_handler
def cloudflare_record_add(ctx, domain, name, type, content, ttl):
pass
@cloudflare_record.command('edit')
@click.option('--ttl', '-t')
@click.argument('domain')
@click.argument('name')
@click.argument('type')
@click.argument('content')
@click.pass_obj
@__cf_error_handler
def cloudflare_record_edit(ctx, domain):
pass
command_uptimerobot.py
@cli.group()
@click.pass_context
def uptimerobot(ctx):
pass
@uptimerobot.command('add')
@click.option('--alert', '-a', default=True)
@click.argument('name')
@click.argument('url')
@click.pass_obj
def uptimerobot_add(ctx, name, url, alert):
pass
@uptimerobot.command('delete')
@click.argument('names', nargs=-1, required=True)
@click.pass_obj
def uptimerobot_delete(ctx, names):
pass
I'm not an click expert, but it should work by just importing your files into the main one. I would move all commands in separate files and have one main file importing the other ones. That way it is easier to control the exact order, in case it is important for you. So your main file would just look like:
The downside of using
CommandCollection
for this is that it merges your commands and works only with command groups. The imho better alternative is to useadd_command
to achieve the same result.I have a project with the following tree:
Each subcommand has its own module, what makes it incredibly easy to manage even complex implementations with many more helper classes and files. In each module, the
commands.py
file contains the@click
annotations. Examplegroup2/commands.py
:If necessary, you could easily create more classes in the module, and
import
and use them here, thus giving your CLI the full power of Python's classes and modules.My
cli.py
is the entry point for the whole CLI:With this setup, it is very easy to separate your commands by concerns, and also build additional functionality around them that they might need. It has served me very well so far...
Reference: http://click.pocoo.org/6/quickstart/#nesting-commands
Suppose your project have the following structure:
Groups are nothing more than multiple commands and groups they can be nested. You can separate your groups into modules and import them on you
init.py
file and add them to thecli
group using the add_command.Here is a
init.py
example:You have to import the cloudflare group which lives inside the cloudflare.py file. Your
commands/cloudflare.py
would look like this:Then you can run the cloudflare command like this:
This information is not very explicit on the documentation but if you look at the source code, which is very well commented, you can see how groups can be nested.
I'm looking for something like this at the moment, in your case is simple because you have groups in each of the files, you can solve this problema as explained in the documentation:
In the
init.py
file:The best part of this solution is that is totally compliant with pep8 and other linters becaouse you don't need to import something you wouldn't use and you dont need to import * from anywhere.
I hope this helps and good look with your cli.