使用argparse指定默认文件名,但在--help时不打开它们?

13

假设我有一个脚本,用于处理文件。它会在命令行上接收该文件的名称,如果没有提供该参数,则会默认为已知的文件名(例如content.txt)。使用Python的 argparse 模块,我可以这样写:

parser = argparse.ArgumentParser(description='my illustrative example')
parser.add_argument('--content', metavar='file', 
                     default='content.txt', type=argparse.FileType('r'),
                     help='file to process (defaults to content.txt)')
args = parser.parse_args()
# do some work on args.content, which is a file-like object

这个方法很好用。唯一的问题是,如果我运行python myscript --help,如果文件不存在,就会出现ArgumentError(这似乎很合理),并且帮助文本不会显示。如果用户只想要--help,我宁愿它不尝试打开文件。有没有办法做到这一点?我知道我可以把参数作为字符串,并稍后自己处理打开文件(我一直在这样做),但让argparse来处理会更方便。


好的,看起来你必须在这里做出一个决定。你可以让默认文件可用(因为它是“默认的”),或者像你自己建议的那样自己打开文件。另外,似乎你可以使用stdin作为默认值,但你将无法指定默认文件名,如果你不想填充该文件,则可能不是一个坏选择。 - Manny D
3
据我所知,这个问题在Python的新版本中已经被修复了,你提供的片段可以正常工作。 - matejcik
4个回答

13

您可以继承 argparse.FileType

import argparse
import warnings

class ForgivingFileType(argparse.FileType):
    def __call__(self, string):
        try:
            super(ForgivingFileType,self).__call__(string)
        except IOError as err:
            warnings.warn(err)

parser = argparse.ArgumentParser(description='my illustrative example')
parser.add_argument('--content', metavar='file', 
                     default='content.txt', type=ForgivingFileType('r'),
                     help='file to process (defaults to content.txt)')
args = parser.parse_args()

这种方法可行,而且无需涉及私有方法,比如ArgumentParser._parse_known_args


10

观察argparse代码,我发现:

  • ArgumentParser.parse_args调用parse_known_args并确保没有任何待解析的参数。
  • ArgumentParser.parse_known_args设置默认值并调用ArgumentParser._parse_known_args

因此,解决方法是直接使用ArgumentParser._parse_known_args来检测-h,然后像往常一样使用ArgumentParser.parse_args

import sys, argparse
parser = argparse.ArgumentParser(description='my illustrative example', argument_default=argparse.SUPPRESS)
parser.add_argument('--content', metavar='file',
                     default='content.txt', type=argparse.FileType('r'),
                     help='file to process (defaults to content.txt)')
parser._parse_known_args(sys.argv[1:], argparse.Namespace())
args = parser.parse_args()

请注意,ArgumentParser._parse_known_args 需要两个参数:命令行参数和命名空间。

当然,我不建议使用这种方法,因为它利用了内部的 argparse 实现,这可能会在将来发生变化。但是,我认为它并不太混乱,所以如果您认为维护风险可控,仍然可以使用它。


看起来这个问题没有一个很好的解决方案,但对于一次性脚本来说这样做就可以了。谢谢! - Ismail Badawi

2

使用标准输入作为默认值:

parser.add_argument('file', default='-', nargs='?', type=argparse.FileType('r'))

1

也许你可以在add_argument调用中定义自己的typeaction,检查文件是否存在并返回文件句柄(如果存在)和None(或其他内容)。

这需要你自己编写一些代码,但如果默认值不能始终使用,你可能迟早要进行一些检查。正如Manny D所说,你可能需要重新考虑默认值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接