how to debug python click cli application?
Asked Answered
U

6

39

I have built a cli application using the click library in python. There is no documentation on how to debug commands.

Without click, its easy to just debug python files in IDE, but when we use click, the commands need to be run through console_scripts setup in setup.py.

Ulani answered 30/3, 2017 at 10:51 Comment(0)
M
52

This is not well documented, but you can call your command functions directly, and thus can run the code in a debugger:

Sample Code:

import click

@click.command()
@click.option('--my_arg', default=1, help='a number')
def my_command(my_arg):
    click.echo("my_arg='%d'" % my_arg)

if __name__ == '__main__':
    my_command(['--my_arg', '3'])

Result:

my_arg='3'
Murk answered 31/3, 2017 at 18:53 Comment(8)
How to pass multiple arguments? Especially if one of them is a flag? What if there is another @click.option("--full", is_flag=True). What would be the correct syntax for it to be passed to my_command? If I add another array with ["--full", True] the execution fails. I would need this in order to debug.Doretha
It is just a list. Basically, think like cmdlinestr.split()Murk
Stephen, thanks! Lint complains that the second parameter is missing and that was misleading, but it works!Doretha
Does this invocation remain trivial if the click application is using subcommands and other advanced patterns? The need to debug within an IDE correlates to developing non-trivial applications.Czechoslovakia
@JordanStefanelli, Yes, it stays the same.Murk
Taking the approach from the answer and provided that you add the import sys, the call to the click Command in the last conditional, by simply adding sys.argv[1:] as parameter, command would receive the complete set of parameters as it had been invoked from the shell.Piperine
@blamblam, This is test code. Much better to be complete here... Additionally, in an actual program you can leave the sys.argv[1:] off completely and click will do that for you.Murk
I'm using Typer and I focused on the decorators so much that I didn't notice you're just directly calling your function in the main condition. Obviously, this also works for TyperSector
G
8

I'm a couple years late to the party, but in case anyone else comes here looking for an answer like I just did:

Calling your function with CliRunner.invoke() will return a "Result" object with an "exc_info" attribute. You can feed that to traceback.print_exception() like so:

runner = CliRunner()
result = runner.invoke(my_command)
traceback.print_exception(*result.exc_info)
Gaff answered 25/11, 2019 at 21:31 Comment(0)
C
4

The setup.py generates:

  • console_script.exe
  • console_script-script.py

from cmdline:

console_app --help

expands out to the IDE configuration cmd:

python <absolute path to>\console_app-script.py --help

Tested in PyCharm 2018.2 - you can set and hit breakpoints and preserve the expected cmdline/arg paradigm.

Czechoslovakia answered 28/2, 2019 at 11:32 Comment(0)
C
3

I have found that writing tests is a great way to debug Click CLI applications: http://click.pocoo.org/5/testing/

Start really simple with your function and a test, then add to it, making sure the test tells you what you need it to...

Also, setting defaults helps:

def run_files(input_file='/path/to/input_file', output_file='/path/to/output_file'): click.echo(input_file, output_file)

I also generally set up logging and log everything when I begin:

logging.basicConfig(format='%(levelname)s %(message)s', level=logging.DEBUG)

Then sprinkle these throughout (IU have utilities with timestamp stuff, but this is not required):

logging.info('[{0}] Blah blah selected...'.format(
             utils.get_timestamp()))

You can do this with print or click.echo too.

Chamomile answered 5/5, 2017 at 16:47 Comment(0)
R
3

You can use pdb like this, put inside the code you want to debug:

import pdb
pdb.set_trace()

For example:

import click

@click.command()
def hello():
    msg = "hi"
    num = 3

    import pdb
    pdb.set_trace()

    click.echo('Hello1!')
    click.echo('Hello2!')
    click.echo(msg + num)

if __name__ == '__main__':
    hello()

Then you can use pdb:

$ python hello.py 
> /home/eduardo/w/ifpb/cozer/hello.py(11)hello()
-> click.echo('Hello1!')
(Pdb) msg
'hi'
(Pdb) num
3
(Pdb) msg = hello
(Pdb) b 13
Breakpoint 1 at /home/eduardo/w/ifpb/cozer/hello.py:13
(Pdb) c
Hello1!
Hello2!
> /home/eduardo/w/ifpb/cozer/hello.py(13)hello()
-> click.echo(msg + num)
(Pdb) c
Traceback (most recent call last):
  File "hello.py", line 16, in <module>
    hello()
  File "/home/eduardo/w/ifpb/cozer/venv/lib/python3.5/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/eduardo/w/ifpb/cozer/venv/lib/python3.5/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/eduardo/w/ifpb/cozer/venv/lib/python3.5/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/eduardo/w/ifpb/cozer/venv/lib/python3.5/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "hello.py", line 13, in hello
    click.echo(msg + num)
TypeError: unsupported operand type(s) for +: 'Command' and 'int'

After the error we can try again a solution:

$ python hello.py 
> /home/eduardo/w/ifpb/cozer/hello.py(11)hello()
-> click.echo('Hello1!')
(Pdb) msg
'hi'
(Pdb) num
3
(Pdb) b 13
Breakpoint 1 at /home/eduardo/w/ifpb/cozer/hello.py:13
(Pdb) c
Hello1!
Hello2!
> /home/eduardo/w/ifpb/cozer/hello.py(13)hello()
-> click.echo(msg + num)
(Pdb) num = str(num)
(Pdb) c
hi3
Ripple answered 2/9, 2019 at 14:38 Comment(0)
N
-2

If you are looking to debug the modules/classes that are executed after click command functions.

An affordable solution is to create a script that simulates the flow that is performed after executing the command.

Then by placing debug points in the required lines you can achieve the debug functionality.

This is what I figured to do after a brief web search.

Newmint answered 5/6, 2017 at 6:37 Comment(1)
Would you mind providing some code examples to show how trivial and affordable this really is?Czechoslovakia

© 2022 - 2024 — McMap. All rights reserved.