2017-05-30 54 views
0

我想支持一个命令行界面,用户可以在其中声明任意数量的样本,并且每个样本对应一个或多个输入文件。事情是这样的:用Python支持任意数量的相关命名参数argparse

$ myprogram.py \ 
     --foo bar \ 
     --sample1 input1.tsv \ 
     --sample2 input2a.tsv input2b.tsv input2c.tsv \ 
     --sample3 input3-filtered.tsv \ 
     --out output.tsv 

的想法是,选项键将匹配模式--sample(\d+),并且每个按键将消耗所有后续参数作为选项的值,直到遇到下一个---前缀标志。对于显式声明的参数,这是​​模块通过nargs='+'选项支持的常见用例。但是因为我需要支持任意数量的参数,所以我不能明确地声明它们。

parse_known_args命令将允许我访问所有用户提供的参数,但那些未显式声明的参数不会被分组到索引数据结构中。对于这些我需要仔细检查参数列表,然后看看有多少后续值对应于当前标志,等等。

有没有什么办法可以解析这些选项而不必实质上重新实现参数解析器的大部分(几乎)从头开始?

+0

你有没有考虑过建立argparser?计算命令行上'--sample'的数量并使用它来构建适当的argparser?这种环岛,但它可以让argparse完成繁重的工作。 – TemporalWolf

回答

1

如果你可以用一个稍微不同的语法,即生活:

$ myprogram.py \ 
    --foo bar \ 
    --sample input1.tsv \ 
    --sample input2a.tsv input2b.tsv input2c.tsv \ 
    --sample input3-filtered.tsv \ 
    --out output.tsv 

其中参数名不包含数字,但仍进行分组,试试这个:

parser.add_argument('--sample', action='append', nargs='+') 

它产生一个列表,即。--sample x y --sample 1 2会产生Namespace(sample=[['x', 'y'], ['1', '2']])

+0

我之前使用过'action =“append”',但从来没有在这种情况下使用过。伟大的建议! –

0

你可能会用click而不是​​来做这种事情。

引述:

$ click_

点击是一个Python包的组合的方式创造出美丽的命令行接口 根据需要尽可能少的代码。 这是“命令行界面创建工具包”。它非常可配置,但配有合理的默认值。

它旨在使编写命令行工具的过程变得快速并且有趣,同时还防止由于无法实现预期的CLI API而导致的任何挫败感。

单击三点:

  • 命令的任意嵌套
  • 自动帮助页面生成
  • 支持在运行时

    子的延迟加载阅读文档在http://click.pocoo.org/

一个点击的重要特点是构造子命令的能力,(有点像使用Git或图像魔术秘密),它应该让你组织你的命令行:

myprogram.py \ 
    --foo bar \ 
    --sampleset input1.tsv \ 
    --sampleset input2a.tsv input2b.tsv input2c.tsv \ 
    --sampleset input3-filtered.tsv \ 
    combinesets --out output.tsv 

或者连:

myprogram.py \ 
    --foo bar \ 
    process input1.tsv \ 
    process input2a.tsv input2b.tsv input2c.tsv \ 
    process input3-filtered.tsv \ 
    combine --out output.tsv 

这可能是清洁剂,在这种情况下,您的代码必须的参数调用--foo--out和函数调用processcombine进程将使用指定的输入文件(S)被调用,并没有对合并米。

+1

除非你真的可以通过点击来展示如何做到这一点 - 或者至少指出了要使用的功能 - 但这并不算真正的答案。 – jwodder

1

将该数字或关键字设置为单独的参数值并在嵌套列表中收集相关参数会更简单。

import argparse 

parser = argparse.ArgumentParser() 
parser.add_argument('--foo') 
parser.add_argument('--out') 
parser.add_argument('--sample', nargs='+', action='append', metavar=('KEY','TSV')) 

parser.print_help() 

argv = "myprogram.py \ 
     --foo bar \ 
     --sample 1 input1.tsv \ 
     --sample 2 input2a.tsv input2b.tsv input2c.tsv \ 
     --sample 3 input3-filtered.tsv \ 
     --out output.tsv" 
argv = argv.split() 
args = parser.parse_args(argv[1:]) 
print(args) 

生产:

1031:~/mypy$ python3 stack44267794.py -h 
usage: stack44267794.py [-h] [--foo FOO] [--out OUT] [--sample KEY [TSV ...]] 

optional arguments: 
    -h, --help   show this help message and exit 
    --foo FOO 
    --out OUT 
    --sample KEY [TSV ...] 
Namespace(foo='bar', out='output.tsv', 
    sample=[['1', 'input1.tsv'], 
      ['2', 'input2a.tsv', 'input2b.tsv', 'input2c.tsv'], 
      ['3', 'input3-filtered.tsv']]) 

已经有大约收集一般key:value对的问题。​​没有什么可以直接支持的。已经提出了各种各样的东西,但都归结为你自己解析对。

Is it possible to use argparse to capture an arbitrary set of optional arguments?

您已经添加了并发症,论据每个键的数量是可变的。这就排除了处理'--sample1 = input1'作为简单的字符串。

​​已经延伸了众所周知的POSIX命令行标准。但是如果你想要超越这个范围,那么要准备好在(sys.argv)之前或​​(parse_known_argsextras)之后处理参数。

1

正如我在我的评论中提到:

import argparse 

argv = "myprogram.py \ 
     --foo bar \ 
     --sample1 input1.tsv \ 
     --sample2 input2a.tsv input2b.tsv input2c.tsv \ 
     --sample3 input3-filtered.tsv \ 
     --out output.tsv" 

parser = argparse.ArgumentParser() 
parser.add_argument('--foo') 
parser.add_argument('--out') 
for x in range(1, argv.count('--sample') + 1): 
    parser.add_argument('--sample' + str(x), nargs='+') 
args = parser.parse_args(argv.split()[1:]) 

给出:

print args 
Namespace(foo='bar', out='output.tsv', sample1=['input1.tsv'], sample2=['input2a.tsv', 'input2b.tsv', 'input2c.tsv'], sample3=['input3-filtered.tsv']) 

与真实sys.argv你可能不得不更换argv.count与稍长' '.join(sys.argv).count('--sample')

这种方法的主要缺点是自动帮助生成w生病不包括这些领域。

+1

您的代码使用字符串'argv',但不能与已分割的'sys.argv'一起使用。帮助将列出命令行中给出的任何'sample#',以及'-h'。但是为了显示一个通用的'sample#',你必须使用一个自定义的'usage'参数。 – hpaulj