Python 使用argparse指定文件扩展名

Python 使用argparse指定文件扩展名,python,argparse,file-extension,Python,Argparse,File Extension,我想用argparse指定几个文件扩展名 我已经尝试了下面的代码,但它不工作。如何使用argparse指定多个文件扩展名 parser.add_argument('file', action = 'store', type = argparse.FileType('r'), choices=["*.aaa", "*.bbb"]) 编辑:我使用字符串类型而不是文件类型找到了自己的解决方案: def input_ok(string): if not os.path.exists(strin

我想用argparse指定几个文件扩展名

我已经尝试了下面的代码,但它不工作。如何使用argparse指定多个文件扩展名

parser.add_argument('file', action = 'store', type = argparse.FileType('r'), choices=["*.aaa", "*.bbb"])
编辑:我使用字符串类型而不是文件类型找到了自己的解决方案:

def input_ok(string):
    if not os.path.exists(string):
        raise argparse.ArgumentTypeError("Filename %r doesn\'t exists in this directory." % string)

    if string[-4:] != ".aaa" and string[-4:] != ".bbb":
        raise argparse.ArgumentTypeError("%r is not a .aaa or a .bbb file." % string)
return string


问题的关键是如何工作。Argparse首先构建一个传递参数的列表并进行类型转换,然后使用
in
操作符检查选项中是否包含参数。这意味着它不会进行任何模式匹配(匹配
'*.aaa'
),而是检查字符串是否相等。相反,我们可以制作自己的容器来传递选择

如果不使用
argparse.Filetype
,它看起来是这样的。Argparse还需要容器具有
\uuuuu iter\uuuu
,以生成帮助消息的Metavar元组

class Choices():
    def __init__(self, *choices):
        self.choices = choices

    def __contains__(self, choice):
        # True if choice ends with one of self.choices
        return any(choice.endswith(c) for c in self.choices)

    def __iter__(self):
        return iter(self.choices)

parser.add_argument('file', action='store', choices=Choices('.aaa', '.bbb'))
通过更改
\uuuu包含的内容来满足您的需要,您可以将此想法扩展到几乎任何事情。例如,如果您还传递了
type=argparse.FileType('r')
,则argparse将在检查包含之前转换为文件对象

def __contains__(self, choice):
    # choice is now a file object
    return any(choice.name.endswith(c) for c in self.choices)


顺便说一句,这就是我讨厌argparse的原因。它过于复杂,并且试图做得比它应该做的更多。我认为不应该在参数解析器中进行验证。自己使用并验证东西。

接受字符串,然后自己进行验证没有什么错。有时,您需要检查文件名是否正确,但要等到以后才能打开它(例如,将打开(文件名)作为f:
)使用
)。这可能是最简单的方法

kalhartt的
选项
类的另一种选择是使用
os.path
glob
获取允许文件的列表

p.add_argument('file',choices=glob.glob('*.txt'))
In [91]: p.parse_args('test.txt'.split())
Out[91]: Namespace(file='test.txt')
这样做的一个问题是,帮助和错误消息可能过长,列出了所有允许的文件名

选项
不适用于
文件类型
。这是因为在打开文件后,它会根据选项进行测试

p.add_argument('file',choices=[open('test.txt')],type=argparse.FileType('r'))
p.parse_args('test.txt'.split())
# usage: python [-h] {<open file 'test.txt', mode 'r' at 0xa102f98>}
# error: argument file: invalid choice: <open file 'test.txt', mode 'r' at 0xa102f40>
# (choose from <open file 'test.txt', mode 'r' at 0xa102f98>)

argparse
为您提供了一些参数验证工具,但不要求您使用它们<代码>文件类型
是一种方便的功能,用于常见的脚本应用程序。如果它不适合您的应用程序,您不必使用它。
选项也是如此。
p.add_argument('file',choices=[open('test.txt')],type=argparse.FileType('r'))
p.parse_args('test.txt'.split())
# usage: python [-h] {<open file 'test.txt', mode 'r' at 0xa102f98>}
# error: argument file: invalid choice: <open file 'test.txt', mode 'r' at 0xa102f40>
# (choose from <open file 'test.txt', mode 'r' at 0xa102f98>)
class FileTypeWithExtension(argparse.FileType):
    def __init__(self, mode='r', bufsize=-1, extension=None):
        self._extension = extension
        super(FileTypeWithExtension, self).__init__()
    def __call__(self, string):
        if string != '-' and self._extension:
            if not string.endswith(self._extension):
               # just testing against one extension for now
               raise argparse.ArgumentTypeError('wrong extension')
        return super(FileTypeWithExtension, self).__call__(string)

p.add_argument('file',type=FileTypeWithExtension('r',extension='txt'))
p.parse_args('test.tst'.split())
#usage: ipython [-h] file
#ipython: error: argument file: wrong extension

p.parse_args('test.txt'.split())
# Namespace(file=<open file 'test.txt', mode 'r' at 0xa13ce90>)