使用argparse,我该如何处理" chdir" fromfile扩展前的参数?

时间:2016-05-19 03:43:44

标签: python argparse fromfile

我想支持一个子命令CLI模型,就像git使用的那样。我遇到问题的特定位是"更改目录"选项。与git类似,我想要一个-C DIR选项,在执行子命令之前将程序更改为指定的目录。使用子解析器并不是真正的问题,我还希望在解析期间应用argparse.ArgumentParser(fromfile_prefix_chars='@')参数之后使用-C DIR机制

这里的rub:fromfile参数扩展由argparse 在所有其他参数处理之前执行。因此,任何此类fromfile参数必须在调用解析器时使用绝对路径或相对于CWD的路径。我想要绝对的道路;我"需要" 使用相对于fromfile选项的-C DIR路径。我写了自己的{{1}做明显的事。它工作正常,但由于class ChdirAction(argparse.Action)论证已经扩展,它没有给我我想要的东西。 (在发现了这个 not-what-I-want 行为之后,我查看了fromfile并发现同样的挫败感嵌入了冷酷,坚硬,无情的代码中。)

这是一个可能有助于解释我想要的目录图:

python3.5/argparse.py

在解析命令行参数时,考虑CWD何时为/ foo / aaa / iii / arg.txt | | | + jjj / arg.txt | | | + arg.txt | + bbb / iii / arg.txt | + jjj / arg.txt aaa。如果我运行bbb之类的东西 我希望解析器使用prog -C ./iii @arg.txt中的参数展开@arg.txt。实际发生的是/foo/aaa/iii/arg.txtfromfile的内容扩展。当CWD为/foo/aaa/arg.txt时,这就是"错误"文件;当/foo/aaa引发错误时,错误:[错误2]没有这样的文件或目录:' arg.txt'"

更一般地说,/foo/bbb应该从prog -C ./DIR @arg.txt扩展,即使/foo/aaa/DIR/arg.txt具有" up-directory"零件,例如fromfile应该从prog -C ./iii @../arg.txt扩展。

如果这种行为可以发生,那么我可以/foo/aaa/arg.txt-C DIR中的任何一个,并从公共命令行构造中获得良好的行为。

如上所述,我的问题不是很大的问题。如果可以提供{aaa,bbb}/{iii,jjj},在参数解析后由-C DIR实现,那么我也可以构造适当的os.chdir(DIR)参数。它们可以是解析时CWD 的绝对值或相对值(在任何fromfile生效之前)。这可能看起来像:

-C DIR

我不喜欢它,但没关系。 REAL 问题是我使用的实际更改目录参数更像是cd /foo/aaa; prog -C ./DIR @arg.txt @./DIR/arg.txt。在我的实际问题案例中,PATTERN可以是一个简单的路径(绝对路径或相对路径)。或者,它可能是一个glob模式,或者是一个具有"非平凡的"解析逻辑以查找-C PATTERN的实际目录。在这种情况下(我正在努力),我无法让程序的调用者解析os.chdir(DIR)路径的实际位置。

实际上,我可以,但这会给调用者带来不适当的负担。而且,当那个调用者是一个Eclipse启动器时,我真的没有必要的控制流功能。所以,它回到让程序照顾它自己的需求;一个更好的抽象,但 我该如何实现呢?

2 个答案:

答案 0 :(得分:0)

即使我正在充实问题,我想出了一个主意。所以我尝试了它,它有点,有点,好吧(ish)。我可以得到一个我真正想要的限制版本,但它对我来说已经足够好了(现在),所以我想我也可以分享一下。对你来说也可能已经足够了。更好的是,它可能从某个地方引出一个真正的解决方案,也许S.Bethard?

我的hack是分两个阶段进行解析:第一个,只需通过-C PATTERN 获取ArgumentParser.parse_known_args(...)参数而不启用fromfile机制。如果第一个(最小)解析的结果产生目录更改参数,那么我处理它。如果指定了多个-C PATTERN,程序将中止,或者无法明确地解析PATTERN。

然后,我使用一个完全独立的ArgumentParser对象,配置了我真正想要的完整参数规范集,并在启用fromfile机制的情况下解析它 < / p>

有一些猴子业务可以使--help参数起作用(设置正确的冲突解决策略,然后只接受第一个解析器中的arg只是传递给第二个,实际上有所有& #34;真实&#34;论证规范)。此外,第一个解析器应该支持与第二个解析器相同的详细/安静选项,尊重它们的设置并且从第一个解析器传递到第二个解析器。

这是我的应用程序级arg解析器方法的简化版本。它在第一个解析器阶段不支持verbose / quiet选项。我已经忽略了-C PATTERN如何解析为实际目录的复杂性。此外,我删除了第二个解析器的参数规范的大部分,只留下了第二个解析器的-C PATTERN参数(--help输出所需)。

注意:两个解析器都有一个-C PATTERN参数。在chdirParser它是有意义的;仅在argParser中出现,因此它会显示在帮助输出中。对于冗长/安静的选项应该做类似的事情 - 可能不是那个三角形,但它对我来说并不重要,所以我不介意总是报告目录的更改,即使在安静模式下也是如此。 / p>

def cli_args_from_argv():

    import argparse
    import glob
    import os
    import sys

    chdirParser = argparse.ArgumentParser(conflict_handler='resolve')
    chdirParser.add_argument("-C",           dest="chdir_pattern", action="append"    , default=None)
    chdirParser.add_argument("--help", "-h", dest="help",          action="store_true", default=False)

    (partial, remainder) = chdirParser.parse_known_args()

    if partial.help:
        remainder = ['--help']

    elif partial.chdir_pattern:
        if len(partial.chdir_pattern) > 1:
            print(r'Too many -C options - at most one may be given, but received: {!r}'.format(partial.chdir_pattern), file=sys.stderr)
            sys.exit(1)

        pattern  = partial.chdir_pattern[0]
        resolved_dir = pattern
        if os.path.exists(resolved_dir):
            resolved_dir = pattern

        else:

            ### ELIDED: resolution of pattern into an unambiguous and existing directory

            if not resolved_dir:
                print("Failed to resolve -C {!r}".format(pattern), file=sys.stderr)
                sys.exit(1)

        print("Changing to directory: {!r}".format(resolved_dir))
        print("");

        os.chdir(target_dir)


    argParser = argparse.ArgumentParser(usage="usage: PROG [common-args] SUBCMD [subcmd-args]", fromfile_prefix_chars=':')

    ### ELIDED: a bunches of add_argument(...)

    argParser.add_argument("-C", dest="chdir_spec", action="store", default=None, help="Before anything else, chdir to SPEC", metavar="SPEC")

    return argParser.parse_args(args=remainder)

我觉得这可能是更好的方式......你知道吗?

答案 1 :(得分:0)

我认为resolve位可以替换为

chdirParser = argparse.ArgumentParser(add_help=False)

并省略-h定义并保存。让第二个解析器处理sys.argv不变(因为你包括(但忽略)-C参数)。

如果您希望用户使用多个append命令,则len(partial.chdir_pattern) > 1-C dir1 ... -C dir2...的测试应该有效。使用默认store操作的替代方法,最终会保存最后一次重复操作。为什么用户可能会重复-C,为什么要关心?通常我们只是忽略重复。

您可以替换

print("Failed to resolve -C {!r}".format(pattern), file=sys.stderr)
sys.exit(1)

parser.error("Failed to resolve -C {!r}".format(pattern)')

它打印用法(只有-C) and does an sys.exit(2)`。不完全相同,但可能足够接近。

对于第二个解析器,-C可能会被简化(使用默认值):

argParser.add_argument("-C", "--chdir-spec", help="Before anything else, chdir to SPEC", metavar="SPEC")

并使用完整的sys.argv

return argParser.parse_args()

否则,使用2个解析器是有意义的,因为更改的目录中存在fromfile(并且您希望忽略初始目录中的任何此类文件)。

我想可能是:arg.txt字符串,命令行会在第一个解析器中给出问题。但是对于parse_known_args,它只会将其视为一个未知的位置。但是测试中的证明。