我想支持一个子命令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.txt
从fromfile
的内容扩展。当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启动器时,我真的没有必要的控制流功能。所以,它回到让程序照顾它自己的需求;一个更好的抽象,但 我该如何实现呢?
答案 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
,它只会将其视为一个未知的位置。但是测试中的证明。