I'm writing a python script, that should behave like a typical shell and providing some self written functions. It is working quite well already, but it always exits after a successful command, so that it has to be started again to perform a second task. How can I make it, so it doesn't finish with exit code 0 but returns to shell awaiting new input? How would I have to implement exit methods then? Following example always exits after typing print-a or print-b:
import click
import click_repl
from prompt_toolkit.history import FileHistory
import os
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
ctx.invoke(repl)
@cli.command()
def print_a():
print("a")
@cli.command()
def print_b():
print("b")
@cli.command()
def repl():
prompt_kwargs = {
'history': FileHistory(os.path.expanduser('~/.repl_history'))
}
click_repl.repl(click.get_current_context(), prompt_kwargs)
def main():
while True:
cli(obj={})
if __name__ == "__main__":
main()
(And a bonus question: In the cmd package it is possible to customize the >
prompt tag, is this possible with click to? So that it's something like App>
instead?)
Use the standalone_mode
argument, try this:
rv = cli(obj={}, standalone_mode=False)
When parsing failed, the code above will throw a UsageError
. When --help
was passed, rv
will be the integer 0
. In most other cases the return value of the function that handles the command is returned, although there are a bunch of exceptions and the behavior in general is quite complex, more explanations here:
https://click.palletsprojects.com/en/master/commands/#command-return-values
The advantage of this approach is that you can use return values from command handlers. The disadvantage is that you lose the pretty printed help message when parsing failed (maybe there is way to restore it?).
Another option is to not use standalone_mode
and instead wrap your call to cli
in a try/except
block where you catch a SystemExit
:
try:
cli(obj={})
except SystemExit as e:
if e.code != 0:
raise
By catching SystemExit
you can stop the program exit process initiated by click. If the command parsed succesfully then SystemExit(0)
is caught. Note again that parsing --help
also counts as a 'successfull' parse, and therefore also returns SystemExit(0)
.
The disadvantage of this approach is that you cannot use the return value of a command handler, which makes it more difficult to know when --help
was passed. The upside is that all help messages to the console are restored.
I should also note that SystemExit
inherits from BaseException
but not from Exception
. So to actually catch SystemExit
you can either catch it directly or catch BaseException
.
You could check out click-shell
, which is a wrapper for click
and the python cmd
module. It supports auto completion and help from docstrings out of the box.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With