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.
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'm using the Click to build a command-line application. For audit purposes, need to get access full original command that the user executed. I had no luck getting the original user command before the Click parsing arguments. I couldn't find any similar use case in their documentation. something like :
#click.group()
#click.option('--debug/--no-debug', default=False)
#click.pass_context
def cli(ctx, debug):
if debug:
# print('Original unparssed command with arguments')
I wanted to check if anyone knows a trick to get that before opening an issue on the github.
Is there a way to group multiple commands, each with their own different parameters under a single function.
At first glance, a MultiCommand or Group might seem like a natural way of doing what I'd like, i.e. have a single main command act as the Group (and pass the invoke_without_command=True flag) then nest auxiliary Commands beneath it as subcommands. However this doesn't quite have the behavior that I'd want, since I'd like all the options from all commands to be able to be specified without explicitly invoking a subcommand. Additionally, using a Group would also not display the help text of the subcommands without invoking the subcommand on the command line as well.
I guess what I'd ideally like to have is a way to group multiple commands together without the nesting inherent to Click's Group API.
Sorry if this question might be somewhat general. Any help, ideas or tips that can point me in the right direction would be much appreciated.
Here's an outline of what I'd like (file name: cli_test.py):
import click
#click.command()
#click.option('--db-file', type=click.File(mode='r'))
def db_reader(db_file):
click.echo(db_file)
#click.command()
#click.option('--xval', type=float)
#click.option('--yval', type=float)
def get_vals(xval, yval):
return xval, yval
#click.command()
#click.option('--absolutize/--no-absolutize')
def flagger(absolutize):
click.echo(absolutize)
#click.command()
def cli_runner():
db = db_reader.main(standalone_mode=False)
vals = flagger.main(standalone_mode=False)
flag = flagger.main(standalone_mode=False)
if __name__ == '__main__':
cli_runner()
Essentially, I'd like a single command that can be run on the CLI (cli_runner in the above example), which would be able to take the parameters of all Click commands called within it, and then dispatch the processing of them to the appropriate Command instance. However as it stands right now, if I were to invoke on the CLI: $ python cli_test.py --xval 4 I'd get the error Error: no such option: --xval. I have also tried playing around with the pass_context and then ctx.invoke() approach, but the same problem exists.
I suppose I could pass parameters from all contained commands into cli_runner, but that would defeat the purpose of what I want to do, which is to ultimately have 3-4 modular "subcommands", that can then be grouped together in different combinations into larger interfaces that serve slightly different use cases.
Question
When using the python click library to create command-line tools, is it possible to pass an unknown number of arguments to a function? I am thinking of something similar to the *args command.
Usecase
I am trying to build a wrapper for catkin and would like to use click for all the nice utilities it comes with. This application should perform some tasks, like changing into the root of the workspace, before calling catkin with the specified command. E.g. catkin build to compile the code.
The problem with this is, that I do not want to explicitly declare every possible compile flag that you can pass to catkin but rather want to only look out for the actual command and pass all the arguments directly to catkin.
Example
What I have found so far, is the possibility to define a last argument with the option nargs=-1 which will collect all succeeding arguments in this variable. This way you can collect for example a couple of file names. This is almost what I am looking for, except that it wont take flags beginning with a dash -. It will from an error saying Error: no such option: -a
#!/usr/bin/python
import click
import subprocess
#click.command()
#click.argument('action', type=click.STRING)
#click.option('--debug', is_flag=True, help='Build in debug mode.')
#click.argument('catkin_args', nargs=-1, type=click.STRING)
def main(action, debug, catkin_args):
""" A wrapper for catkin """
# Do something here ...
if debug:
# Do some more special things...
subprocess.call(["catkin"] + catkin_args)
According to the docs it's possible in click 4.0+; you just need to set the type of your catkin_args to click.UNPROCESSED.
Documentation has an example wrapping timeit like you describe you want to do with catkin.
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.