Context:
I'm having several scripts with loads of sub commands that I'd like to convert to using click
At the moment all these commands do accept -h and --help in order to display help options. I'd like to keep this behavior.
Problem:
click accepts by default --help to display the help text, but not -h
for a click command this can be changed easily by adding.
#click.group()
#click.help_option("--help", "-h")
def cli():
""" the doc string """
enter code here
#cli.command()
#click.help_option("--help", "-h")
def mycommand()
pass
#cli.command()
#click.help_option("--help", "-h")
def mycommand1()
pass
...
However if I'm having tens of commands I have to reapply the decorator line
#click.help_option("--help", "-h")
fort each sub command.
Would there be any trick to avoid having to write this line everywhere?
You need to define a CONTEXT_SETTINGS and use it like this:
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
#click.command(context_settings=CONTEXT_SETTINGS)
def cli():
pass
From the click documentation:
Help Parameter Customization Changelog The help parameter is
implemented in Click in a very special manner. Unlike regular
parameters it’s automatically added by Click for any command and it
performs automatic conflict resolution. By default it’s called --help,
but this can be changed. If a command itself implements a parameter
with the same name, the default help parameter stops accepting it.
There is a context setting that can be used to override the names of
the help parameters called help_option_names.
This example changes the default parameters to -h and --help instead
of just --help:
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
#click.command(context_settings=CONTEXT_SETTINGS) def cli():
pass And what it looks like:
$ cli -h Usage: cli [OPTIONS]
Options: -h, --help Show this message and exit.
Related
#click.command()
#click.option("--hello", type=click.Path(exists=True))
def read_config(hello):
Trying to pass a file as the argument to the command declared above,
At present the command is executed the following way:
python main.py --hello=filename.txt
How do I change the working format of the above command to as follows:
python main.py hello filename.txt
I'm using the latest version of the click library, and I would like to know if there is a way to make the intended change using the same library.
New to the click library, I know how it can be done using Argparse, but click library is preferred.
Use a group to define read_config as a subcommand, then add an argument, not an option, to the subcommand.
#click.group()
def cli():
"""This doesn't really need to do anything."""
#cli.command()
#click.argument('filename', type=click.Path(exists=True))
def hello(filename):
...
if __name__ == '__main__':
cli()
I have the following click code:
#click.group(invoke_without_command=True)
def cli():
click.echo("Starting CallFlow....")
setup_logging()
# ##################----GEN---##################
#cli.command(help="a sub command")
#click.option(
"--folder", help="Tests folder path", type=str, nargs=1,
)
def sub1(folder):
# run some code here
Running my prog name that uses the above cli like this:
prog-name --help
shows me the correct help text:
Usage: prog-name [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
sub1 Help text
But running
prog-name sub1 --help --folder
I get an error that folder requires an argument like so:
Error: --folder option requires an argument
I thought that --help was an eager parameter and gets evaluated first. shouldn't that produce a help text?
From the documentation, the concept of eagerness refers only to the order of execution. Usually, the command line options will be processed in the order they are defined; making options like --help and --version eager means that they will be evaluated first.
If --help were not eager, your example would require --folder to always be passed first, like:
prog-name sub1 --folder test_folder --help
Is there a way to generate (and export) help documentation using click for all commands and subcommands?
For example,
cli --help all --destination help-docs.txt
would generate help for commands and subcommands following the
cli command subcommand
format and put them into the help-docs.txt file.
The only way I can think that I would accomplish this is to use
cli command subcommand --help
on every subcommand that I wanted to generate help for and cat the output to a file, but it would be nice if there where an easier way to accomplish this using Click --help functionality.
This code will do for Click 7, using mostly documented APIs. You'd basically call recursive_help somewhere, e.g. as a separate subcommand, and pass it your top-level group object.
def recursive_help(cmd, parent=None):
ctx = click.core.Context(cmd, info_name=cmd.name, parent=parent)
print(cmd.get_help(ctx))
print()
commands = getattr(cmd, 'commands', {})
for sub in commands.values():
recursive_help(sub, ctx)
Update 2019-10-05:
one way to use this, assuming cli is a click.group, would be:
#cli.command()
def dumphelp():
recursive_help(cli)
Since you are using click package, I know two cool solutions:
Is to use click-man to auto-generate Python click CLI man page.
Is to use md-click to auto-generate Python click CLI help in md file format.
I am using click within a local module and I would like to adjust how the help is displayed:
Currently output with --help:
Usage: __main__.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
foo Foo is a program very nice and pretty...
By default the prog name is __main__.py and the text is trimmed to 78 chars.
I discovered that this can be adjusted using the HelpFormatter class. But I don't know how to use it in this context.
Current Code:
import click
#click.group()
def main(ctx):
pass
#main.command()
def foo():
pass
click.CommandCollection(sources=[main])()
Expected output:
Usage: my_module_name [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
foo Foo is a program very nice and pretty and this sentence is very long.
If you are trying to to avoid the truncation of the help string, this can be accomplished via the short_help parameter. short_help is generally derived from help but truncated. If passed explicitly, the entire string will be displayed.
To display the string my_module_name, that can be passed under the parameter prog_name
Test Code:
import click
#click.group()
def main(ctx):
pass
#main.command(short_help='Foo is a program very nice and pretty and '
'this sentence is very long.')
def foo():
pass
main(['--help'], prog_name='my_module_name')
Results of short_help:
Usage: my_module_name [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
foo Foo is a program very nice and pretty and this sentence is very long.
I'm developing a management script that does a fairly large amount of work via a plethora of command-line options. The first few iterations of the script have used optparse to collect user input and then just run down the page, testing the value of each option in the appropriate order, and doing the action if necessary. This has resulted in a jungle of code that's really hard to read and maintain.
I'm looking for something better.
My hope is to have a system where I can write functions in more or less normal python fashion, and then when the script is run, have options (and help text) generated from my functions, parsed, and executed in the appropriate order. Additionally, I'd REALLY like to be able to build django-style sub-command interfaces, where myscript.py install works completely separately from myscript.py remove (separate options, help, etc.)
I've found simon willison's optfunc and it does a lot of this, but seems to just miss the mark — I want to write each OPTION as a function, rather than try to compress the whole option set into a huge string of options.
I imagine an architecture involving a set of classes for major functions, and each defined method of the class corresponding to a particular option in the command line. This structure provides the advantage of having each option reside near the functional code it modifies, easing maintenance. The thing I don't know quite how to deal with is the ordering of the commands, since the ordering of class methods is not deterministic.
Before I go reinventing the wheel: Are there any other existing bits of code that behave similarly? Other things that would be easy to modify? Asking the question has clarified my own thinking on what would be nice, but feedback on why this is a terrible idea, or how it should work would be welcome.
Don't waste time on "introspection".
Each "Command" or "Option" is an object with two sets of method functions or attributes.
Provide setup information to optparse.
Actually do the work.
Here's the superclass for all commands
class Command( object ):
name= "name"
def setup_opts( self, parser ):
"""Add any options to the parser that this command needs."""
pass
def execute( self, context, options, args ):
"""Execute the command in some application context with some options and args."""
raise NotImplemented
You create sublcasses for Install and Remove and every other command you need.
Your overall application looks something like this.
commands = [
Install(),
Remove(),
]
def main():
parser= optparse.OptionParser()
for c in commands:
c.setup_opts( parser )
options, args = parser.parse()
command= None
for c in commands:
if c.name.startswith(args[0].lower()):
command= c
break
if command:
status= command.execute( context, options, args[1:] )
else:
logger.error( "Command %r is unknown", args[0] )
status= 2
sys.exit( status )
The WSGI library werkzeug provides Management Script Utilities which may do what you want, or at least give you a hint how to do the introspection yourself.
from werkzeug import script
# actions go here
def action_test():
"sample with no args"
pass
def action_foo(name=2, value="test"):
"do some foo"
pass
if __name__ == '__main__':
script.run()
Which will generate the following help message:
$ python /tmp/test.py --help
usage: test.py <action> [<options>]
test.py --help
actions:
foo:
do some foo
--name integer 2
--value string test
test:
sample with no args
An action is a function in the same module starting with "action_" which takes a number of arguments where every argument has a default. The type of the default value specifies the type of the argument.
Arguments can then be passed by position or using --name=value from the shell.