Skip to content
Advertisement

Why can’t I call other functions/commands after running main() from within script?

#!/usr/bin/env python3
import click

@click.command()
@click.option('--desiredrange', '-r', default=1)

def main(desiredrange):
    print(desiredrange)
    print('abcd')

main()
print('!!!!')

Running the above code gives me the following in the terminal:

1

abcd

But I do not get

!!!!

This scenario is true for other variations of code that include ANYTHING after the main() function. The script exits after executing this function. This is also true for any function i.e. if I placed the print(‘!!!!’) inside another function, then called that function after main(), the script exits after main(), ignoring the second function.

If I removed ‘click’, such that the code looks like:

#!/usr/bin/env python3

def main():
    print(1)
    print('abcd')

main()
print('!!!!')

I will get the following printed to terminal:

1

abcd

!!!!

I can execute other functions after main(), as well as other commands. I run this script from terminal using ./script.py (applied chmod +x script.py). I also get no errors from BOTH scenarios.

Why is this?

Advertisement

Answer

The function named main that you defined isn’t actually the one called directly by the line main(). Instead, the two decorators are creating a new value that wraps the function. This callable (I don’t think it’s necessarily a function, but a callable instance of click.core.Command; I’m not digging into the code heavily to see exactly what happens.) calls raises SystemExit in some way, so that your script exits before the “new” main actually returns.

You can confirm this by explicitly catching SystemExit raised by main and ignoring it. This allows the rest of your script to execute.

try:
    main()
except SystemExit:
    pass
print('!!!!')

Remember that decorator syntax is just a shortcut for function application. With the syntax, you can rewrite your code as

import click

def main(desiredrange):
    print(desiredrange)
    print('abcd')

x = main

main = click.command(click.option('--desiredrange', '-r', default=1)(main))

y = main
assert x is not y

main()
print('!!!!')

Since the assertion passes, this confirms that the value bound to main is not your original function, but something else.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement