我正在用python实现一个classic 的CLI工具箱,并 Select click作为参数解析器.添加命令应该只是添加文件.从那里,命令将列在帮助中,依此类推.此零件正在通过click MultiCommand工作.

我还没有实现的是像loglevelconfigfile这样的全球选项.我不希望每个命令都处理选项.我认为大多数全球选项都会创造出某种程度上的全球状态.如何做到这一点,我迷路了.

# __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_contextContext.parent获取根作用域的参数.

设置:chmod +x ./click_example.py

输出:

❯ ./click_example.py admin_cli invite --log-level DEBUG
DEBUG

P、 我正在我的一个项目中使用类似于此模式的东西(vcspull),请参见vcspull/cli/.在它内部,我将log level参数传递给setup_logger(log=None, level='INFO')函数.此来源是麻省理工学院授权的,因此您/任何人都可以免费使用它作为示例.

Python相关问答推荐

基于另一列的GROUP-BY聚合将列添加到Polars LazyFrame

当条件满足时停止ODE集成?

30个非DATETIME天内的累计金额

BeautifulSoup:超过24个字符(从a到z)的迭代失败:降低了首次深入了解数据集的复杂性:

比较两个有条件的数据帧并删除所有不合格的数据帧

TypeError:';Locator';对象无法在PlayWriter中使用.first()调用

try 使用RegEx解析由标识多行文本数据的3行头组成的日志(log)文件

as_index=False groupBy不支持count

如何判断特定的OPC UA node 是否已经存在Asyncua?

如何计算二十面体每条三角形边的中点

如何获得症状表达式的真实部分?

极点在没有Groupby的情况下聚合

Python:比较日期并批量更新某些字段

从html获取元素时出现问题

如何在Pandas 分组处理中执行多个功能

一种处理Django查询集数据的轻量级方法

获取文本文件并创建CSV文件

POLARS:从GROUP_BY列表中 Select 值,并从另一列中 Select 值

用Arpeggio解析单行和多行注释

Pandas 多列数据帧的重采样和内插