← 中文资源

简体中文版本

Click 链式子命令编排

用 Click 的链式命令组编排多步流水线:子命令按序执行共享结果上下文,回调统一收尾,顺序错误显式拒绝。

阅读英文原文

示例说明

数据管道 CLI 的自然形态是 extract、transform、load 串联执行——链式命令组允许一条命令行写多个子命令,按输入顺序依次执行。共享状态放进上下文对象,各步骤读写同一家账本;result_callback 在全部步骤结束后统一收尾输出,避免每个子命令各自为政。执行顺序约束显式校验,乱序即拒绝。

技术要点

  • chain=True 启用子命令串联执行
  • ctx.obj 作为跨步骤共享账本
  • result_callback 统一收尾输出

代码解读

cli 组开启链模式并把 verbose 与结果列表放进 obj;extract 校验源路径存在后记录步骤;transform 先检查前置步骤已执行,再按标志转换数据;result_callback 遍历结果列表输出整条流水线的执行摘要。

使用建议

复杂管道考虑显式 DAG 描述而非命令顺序。共享状态要防步骤间隐式耦合,键名约定清晰。退出码按步骤成败设置供 CI 判断。

代码示例

import click


@click.group(chain=True)
@click.option('--verbose', is_flag=True, help='enable verbose output')
@click.pass_context
def cli(ctx, verbose):
    ctx.ensure_object(dict)
    ctx.obj['verbose'] = verbose
    ctx.obj['results'] = []


@cli.command('extract')
@click.argument('source', type=click.Path(exists=True))
@click.pass_obj
def extract(state, source):
    click.echo(f'extracting {source}')
    state['results'].append(('extract', source))


@cli.command('transform')
@click.option('--uppercase', is_flag=True)
@click.pass_obj
def transform(state, uppercase):
    if not any(step == 'extract' for step, _ in state['results']):
        raise click.UsageError('transform must run after extract')
    value = 'transformed-data'
    if uppercase:
        value = value.upper()
    state['results'].append(('transform', value))
    click.echo(f'transform ok: {value}')


@cli.result_callback()
def process_pipeline(pipeline, state, **kwargs):
    click.echo('pipeline finished:')
    for step, value in state['results']:
        click.echo(f'  {step} -> {value}')


if __name__ == '__main__':
    cli()