使用 Python 创建 shell 命令行应用程序并单击
Creating a shell command line application with Python and Click
我正在使用 click (http://click.pocoo.org/3/) 创建命令行应用程序,但我不知道如何为该应用程序创建 shell。
假设我正在编写一个名为 test 的程序并且我有一个名为 subtest1 和 subtest2[=15 的命令=]
我能够使它在终端上运行,例如:
$ test subtest1
$ test subtest2
但我想的是shell,所以我可以这样做:
$ test
>> subtest1
>> subtest2
这可以通过点击实现吗?
点击也不是不可能,但也没有内置支持。您要做的第一件事是通过将 invoke_without_command=True
传递给组装饰器(如 here). Then your group callback would have to implement a REPL. Python has the cmd 在标准库中执行此操作的框架所描述的那样,使您的组回调在没有子命令的情况下可调用。使 click 子命令可用涉及覆盖 cmd.Cmd.default
,就像下面的代码片段一样。正确获取所有细节,比如 help
,应该可以在几行内完成。
import click
import cmd
class REPL(cmd.Cmd):
def __init__(self, ctx):
cmd.Cmd.__init__(self)
self.ctx = ctx
def default(self, line):
subcommand = cli.commands.get(line)
if subcommand:
self.ctx.invoke(subcommand)
else:
return cmd.Cmd.default(self, line)
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
repl = REPL(ctx)
repl.cmdloop()
@cli.command()
def a():
"""The `a` command prints an 'a'."""
print "a"
@cli.command()
def b():
"""The `b` command prints a 'b'."""
print "b"
if __name__ == "__main__":
cli()
我试图做一些类似于 OP 的事情,但有额外的选项/嵌套 sub-sub-commands。使用内置 cmd 模块的第一个答案在我的案例中不起作用;也许还有更多的摆弄......但我只做了 运行 跨越 click-shell。还没有机会对其进行广泛测试,但到目前为止,它似乎完全按照预期工作。
我知道这太老了,但我也一直在研究 fpbhb 的解决方案以支持选项。我确信这可能需要更多的工作,但这里有一个如何完成的基本示例:
import click
import cmd
import sys
from click import BaseCommand, UsageError
class REPL(cmd.Cmd):
def __init__(self, ctx):
cmd.Cmd.__init__(self)
self.ctx = ctx
def default(self, line):
subcommand = line.split()[0]
args = line.split()[1:]
subcommand = cli.commands.get(subcommand)
if subcommand:
try:
subcommand.parse_args(self.ctx, args)
self.ctx.forward(subcommand)
except UsageError as e:
print(e.format_message())
else:
return cmd.Cmd.default(self, line)
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
repl = REPL(ctx)
repl.cmdloop()
@cli.command()
@click.option('--foo', required=True)
def a(foo):
print("a")
print(foo)
return 'banana'
@cli.command()
@click.option('--foo', required=True)
def b(foo):
print("b")
print(foo)
if __name__ == "__main__":
cli()
现在有一个名为 click_repl 的库可以为您完成大部分工作。我想我会分享我的努力让它发挥作用。
一个困难是您必须将特定命令设置为 repl
命令,但我们可以重新利用@fpbhb 的方法以允许在未提供另一个命令的情况下默认调用该命令。
这是一个完整的示例,支持所有点击选项,具有命令历史记录,并且无需输入 REPL 即可直接调用命令:
import click
import click_repl
import os
from prompt_toolkit.history import FileHistory
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
"""Pleasantries CLI"""
if ctx.invoked_subcommand is None:
ctx.invoke(repl)
@cli.command()
@click.option('--name', default='world')
def hello(name):
"""Say hello"""
click.echo('Hello, {}!'.format(name))
@cli.command()
@click.option('--name', default='moon')
def goodnight(name):
"""Say goodnight"""
click.echo('Goodnight, {}.'.format(name))
@cli.command()
def repl():
"""Start an interactive session"""
prompt_kwargs = {
'history': FileHistory(os.path.expanduser('~/.repl_history'))
}
click_repl.repl(click.get_current_context(), prompt_kwargs=prompt_kwargs)
if __name__ == '__main__':
cli(obj={})
这是使用 REPL 的样子:
$ python pleasantries.py
> hello
Hello, world!
> goodnight --name fpbhb
Goodnight, fpbhb.
并直接使用命令行子命令:
$ python pleasntries.py goodnight
Goodnight, moon.
我正在使用 click (http://click.pocoo.org/3/) 创建命令行应用程序,但我不知道如何为该应用程序创建 shell。
假设我正在编写一个名为 test 的程序并且我有一个名为 subtest1 和 subtest2[=15 的命令=]
我能够使它在终端上运行,例如:
$ test subtest1
$ test subtest2
但我想的是shell,所以我可以这样做:
$ test
>> subtest1
>> subtest2
这可以通过点击实现吗?
点击也不是不可能,但也没有内置支持。您要做的第一件事是通过将 invoke_without_command=True
传递给组装饰器(如 here). Then your group callback would have to implement a REPL. Python has the cmd 在标准库中执行此操作的框架所描述的那样,使您的组回调在没有子命令的情况下可调用。使 click 子命令可用涉及覆盖 cmd.Cmd.default
,就像下面的代码片段一样。正确获取所有细节,比如 help
,应该可以在几行内完成。
import click
import cmd
class REPL(cmd.Cmd):
def __init__(self, ctx):
cmd.Cmd.__init__(self)
self.ctx = ctx
def default(self, line):
subcommand = cli.commands.get(line)
if subcommand:
self.ctx.invoke(subcommand)
else:
return cmd.Cmd.default(self, line)
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
repl = REPL(ctx)
repl.cmdloop()
@cli.command()
def a():
"""The `a` command prints an 'a'."""
print "a"
@cli.command()
def b():
"""The `b` command prints a 'b'."""
print "b"
if __name__ == "__main__":
cli()
我试图做一些类似于 OP 的事情,但有额外的选项/嵌套 sub-sub-commands。使用内置 cmd 模块的第一个答案在我的案例中不起作用;也许还有更多的摆弄......但我只做了 运行 跨越 click-shell。还没有机会对其进行广泛测试,但到目前为止,它似乎完全按照预期工作。
我知道这太老了,但我也一直在研究 fpbhb 的解决方案以支持选项。我确信这可能需要更多的工作,但这里有一个如何完成的基本示例:
import click
import cmd
import sys
from click import BaseCommand, UsageError
class REPL(cmd.Cmd):
def __init__(self, ctx):
cmd.Cmd.__init__(self)
self.ctx = ctx
def default(self, line):
subcommand = line.split()[0]
args = line.split()[1:]
subcommand = cli.commands.get(subcommand)
if subcommand:
try:
subcommand.parse_args(self.ctx, args)
self.ctx.forward(subcommand)
except UsageError as e:
print(e.format_message())
else:
return cmd.Cmd.default(self, line)
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
repl = REPL(ctx)
repl.cmdloop()
@cli.command()
@click.option('--foo', required=True)
def a(foo):
print("a")
print(foo)
return 'banana'
@cli.command()
@click.option('--foo', required=True)
def b(foo):
print("b")
print(foo)
if __name__ == "__main__":
cli()
现在有一个名为 click_repl 的库可以为您完成大部分工作。我想我会分享我的努力让它发挥作用。
一个困难是您必须将特定命令设置为 repl
命令,但我们可以重新利用@fpbhb 的方法以允许在未提供另一个命令的情况下默认调用该命令。
这是一个完整的示例,支持所有点击选项,具有命令历史记录,并且无需输入 REPL 即可直接调用命令:
import click
import click_repl
import os
from prompt_toolkit.history import FileHistory
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
"""Pleasantries CLI"""
if ctx.invoked_subcommand is None:
ctx.invoke(repl)
@cli.command()
@click.option('--name', default='world')
def hello(name):
"""Say hello"""
click.echo('Hello, {}!'.format(name))
@cli.command()
@click.option('--name', default='moon')
def goodnight(name):
"""Say goodnight"""
click.echo('Goodnight, {}.'.format(name))
@cli.command()
def repl():
"""Start an interactive session"""
prompt_kwargs = {
'history': FileHistory(os.path.expanduser('~/.repl_history'))
}
click_repl.repl(click.get_current_context(), prompt_kwargs=prompt_kwargs)
if __name__ == '__main__':
cli(obj={})
这是使用 REPL 的样子:
$ python pleasantries.py
> hello
Hello, world!
> goodnight --name fpbhb
Goodnight, fpbhb.
并直接使用命令行子命令:
$ python pleasntries.py goodnight
Goodnight, moon.