python-click MultiCommand 的全局选项
Global options for python-click MultiCommand
我正在使用 python 实现经典 CLI 工具箱,我选择了 click as my argument parser. Adding a command should just be adding a file. From there the command is listed in the help and so on. This part is working through a click MultiCommand。
我还没有实现的是 loglevel
或 configfile
等全局选项。我不希望每个命令都处理选项。我认为大多数全局选项都会创建某种全局状态。如何实现这一目标,我迷路了。
我还认为官方文档可以很好地涵盖这些内容。
# __init__.py
import pathlib
import click
import os
import typing
class ToolboxCLI(click.MultiCommand):
commands_folder = pathlib.Path.joinpath(
pathlib.Path(__file__).parent, "commands"
).resolve()
def list_commands(self, ctx: click.Context) -> typing.List[str]:
rv = []
for filename in os.listdir(self.commands_folder):
if filename.endswith(".py") and not filename.startswith("__init__"):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(
self, ctx: click.Context, cmd_name: str
) -> typing.Optional[click.Command]:
ns = {}
fn = pathlib.Path.joinpath(self.commands_folder, cmd_name + ".py")
with open(fn) as f:
code = compile(f.read(), fn, "exec")
eval(code, ns, ns)
return ns["cli"]
@click.group(cls=ToolboxCLI)
@click.option("--loglevel")
def cli(loglevel):
"Toolbox CLI "
# commands/admin.py
import click
@click.group() # <- how do i get global options for this command?
def cli():
pass
@cli.command()
def invite():
pass
click_example.py:
#!/usr/bin/env python
import os
import pathlib
import typing
import click
class ToolboxCLI(click.MultiCommand):
commands_folder = pathlib.Path.joinpath(
pathlib.Path(__file__).parent, "commands"
).resolve()
def list_commands(self, ctx: click.Context) -> typing.List[str]:
rv = []
for filename in os.listdir(self.commands_folder):
if filename.endswith(".py") and not filename.startswith("__init__"):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(
self, ctx: click.Context, cmd_name: str
) -> typing.Optional[click.Command]:
ns = {}
fn = pathlib.Path.joinpath(self.commands_folder, cmd_name + ".py")
with open(fn) as f:
code = compile(f.read(), fn, "exec")
eval(code, ns, ns)
return ns["cli"]
@click.group(
cls=ToolboxCLI,
context_settings={
# Step 1: Add allow_interspersed_args to context settings defaults
"allow_interspersed_args": True,
},
)
@click.option("--log-level")
def cli(log_level):
"Toolbox CLI"
if __name__ == "__main__":
cli()
以上:添加 allow_interspersed_args
以便 --log-level
可以在任何地方访问
注:我重命名为--loglevel
-> --log-level
在commands/admin_cli.py:
import click
@click.group() # <- how do i get global options for this command?
@click.pass_context # Step 2: Add @click.pass_context decorator for context
def cli(ctx):
# Step 3: ctx.parent to access root scope
print(ctx.parent.params.get("log_level"))
pass
@cli.command()
@click.pass_context
def invite(ctx):
pass
使用@click.pass_context
and Context.parent
获取根作用域的参数。
设置:chmod +x ./click_example.py
输出:
❯ ./click_example.py admin_cli invite --log-level DEBUG
DEBUG
P.S。我在我的一个项目中使用了类似于此模式的东西(vcspull), see vcspull/cli/. Inside of it I pass the log level param to a setup_logger(log=None, level='INFO')
函数。此源已获得麻省理工学院许可,因此您/任何人都可以自由使用它作为示例。
我正在使用 python 实现经典 CLI 工具箱,我选择了 click as my argument parser. Adding a command should just be adding a file. From there the command is listed in the help and so on. This part is working through a click MultiCommand。
我还没有实现的是 loglevel
或 configfile
等全局选项。我不希望每个命令都处理选项。我认为大多数全局选项都会创建某种全局状态。如何实现这一目标,我迷路了。
我还认为官方文档可以很好地涵盖这些内容。
# __init__.py
import pathlib
import click
import os
import typing
class ToolboxCLI(click.MultiCommand):
commands_folder = pathlib.Path.joinpath(
pathlib.Path(__file__).parent, "commands"
).resolve()
def list_commands(self, ctx: click.Context) -> typing.List[str]:
rv = []
for filename in os.listdir(self.commands_folder):
if filename.endswith(".py") and not filename.startswith("__init__"):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(
self, ctx: click.Context, cmd_name: str
) -> typing.Optional[click.Command]:
ns = {}
fn = pathlib.Path.joinpath(self.commands_folder, cmd_name + ".py")
with open(fn) as f:
code = compile(f.read(), fn, "exec")
eval(code, ns, ns)
return ns["cli"]
@click.group(cls=ToolboxCLI)
@click.option("--loglevel")
def cli(loglevel):
"Toolbox CLI "
# commands/admin.py
import click
@click.group() # <- how do i get global options for this command?
def cli():
pass
@cli.command()
def invite():
pass
click_example.py:
#!/usr/bin/env python
import os
import pathlib
import typing
import click
class ToolboxCLI(click.MultiCommand):
commands_folder = pathlib.Path.joinpath(
pathlib.Path(__file__).parent, "commands"
).resolve()
def list_commands(self, ctx: click.Context) -> typing.List[str]:
rv = []
for filename in os.listdir(self.commands_folder):
if filename.endswith(".py") and not filename.startswith("__init__"):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(
self, ctx: click.Context, cmd_name: str
) -> typing.Optional[click.Command]:
ns = {}
fn = pathlib.Path.joinpath(self.commands_folder, cmd_name + ".py")
with open(fn) as f:
code = compile(f.read(), fn, "exec")
eval(code, ns, ns)
return ns["cli"]
@click.group(
cls=ToolboxCLI,
context_settings={
# Step 1: Add allow_interspersed_args to context settings defaults
"allow_interspersed_args": True,
},
)
@click.option("--log-level")
def cli(log_level):
"Toolbox CLI"
if __name__ == "__main__":
cli()
以上:添加 allow_interspersed_args
以便 --log-level
可以在任何地方访问
注:我重命名为--loglevel
-> --log-level
在commands/admin_cli.py:
import click
@click.group() # <- how do i get global options for this command?
@click.pass_context # Step 2: Add @click.pass_context decorator for context
def cli(ctx):
# Step 3: ctx.parent to access root scope
print(ctx.parent.params.get("log_level"))
pass
@cli.command()
@click.pass_context
def invite(ctx):
pass
使用@click.pass_context
and Context.parent
获取根作用域的参数。
设置:chmod +x ./click_example.py
输出:
❯ ./click_example.py admin_cli invite --log-level DEBUG
DEBUG
P.S。我在我的一个项目中使用了类似于此模式的东西(vcspull), see vcspull/cli/. Inside of it I pass the log level param to a setup_logger(log=None, level='INFO')
函数。此源已获得麻省理工学院许可,因此您/任何人都可以自由使用它作为示例。