调试时模拟argparse命令行参数输入

14

这个线程是前一个线程的延伸,可以在这里找到。 假设我有一段代码,它有两个目的:1)从整数列表中打印最大数字;2)创建一个新目录。

import argparse
import os

parser = argparse.ArgumentParser()
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')

parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=sum,
                    help='sum the integers (default: find the max)')

parser.add_argument("--output_dir", type=str, default="data/xx")

def main(args):
    os.makedirs(args.output_dir)
    print args.accumulate(args.integers)

if __name__=='__main__':
    args = parser.parse_args()  # Disable during debugging @ Run through terminal
    # args = argparse.Namespace(integers = 1, output_dir= 'mydata_223ss32')  # Disable when run through terminal: For debugging process
    main(args)

这些语句可以通过终端执行:

python test_file.py --output_dir data/xxxx 2 2 5 --sum

然而,在调试过程中,我希望跳过终端的使用。可以在这里找到由hpaulj提出的想法,即简单修改

if __name__=='__main__': 

转换为

if __name__=='__main__':
    args = argparse.Namespace(output_dir= 'mydata')  # Disable when run through terminal: For debugging process
    main(args)

然而,在调试过程中,我也想包含一个整数列表。如果同时包含整数列表和dir地址,就会输出以下错误。

args = argparse.Namespace(integers = "2 2 5", output_dir= 'mydata') 

请问我哪里做错了。

先行致谢。


如果您想调试argparse的解析过程,我建议在.parse_args()之前更改sys.argv,而不是跳过解析步骤。 - jedwards
嗨@jedwards。请问如何更改sys.argv?谢谢。 - mpx
sys.argv 是一个列表,其中第一项是脚本的名称,其余的是在调用脚本时传递给脚本的参数。因此,如果执行 ./script.py --output_dir foo 1 2 3 命令,sys.argv 将变为 ['script.py', '--output_dir', 'foo', '1', '2', '3'],你可以在调用 parser.parse_args() 之前随意编辑该列表。 - jedwards
对,因为它本身不包含——那是由你的解析器添加的。 - jedwards
我添加了一个完整描述两种方法的答案。 - jedwards
显示剩余4条评论
2个回答

25

通过稍微更改您的代码:

import argparse
import sys

parser = argparse.ArgumentParser()
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')

parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=sum,
                    help='sum the integers (default: find the max)')

parser.add_argument("--output_dir", type=str, default="data/xx")

def main(args):
    #os.makedirs(args.output_dir)              # XXX: Commented out for debugging
    print(args.accumulate(args.integers))

if __name__=='__main__':
    print(sys.argv)
    args = parser.parse_args()  # Disable during debugging @ Run through terminal
    # args = argparse.Namespace(integers = 1, output_dir= 'mydata_223ss32')  # Disable when run through terminal: For debugging process
    print(args)
    main(args)

我们可以看到,如果我们使用以下命令调用脚本:./test3.py --output_dir foo 1 2 3

我们的输出为:

['test3.py', '--output_dir', 'foo', '1', '2', '3']
Namespace(accumulate=<built-in function sum>, integers=[1, 2, 3], output_dir='foo')
6

因此,如果您想模拟命令行参数,您有两个选项:

1)编辑sys.argv(个人偏好):

if __name__=='__main__':
    # Override sys.argv
    sys.argv = ['test3.py', '--output_dir', 'foo', '1', '2', '3']
    args = parser.parse_args()
    main(args)

2)创建一个Namespace对象:

if __name__=='__main__':    
    #args = parser.parse_args()  # Disable during debugging @ Run through terminal
    args = argparse.Namespace(accumulate=sum, integers=[1,2,3], output_dir='foo')
    main(args)

第二种方法存在两个问题。首先,通过这样做,您会跳过调试过程中非常重要的一部分——调试argparse配置并确保参数被解析为您所期望的格式。其次,你必须在这里定义由argparse配置添加的隐式默认值(例如你的--sum参数/accumulate)。如果采用第一种方法,argparse会处理sys.argv并添加累加器而无需进行任何更改。而在第二种方法中,我们必须添加accumulate=sum才能使其按照预期运行。


1
感谢您详细的解释,并提供一个好的例子。非常感激。由于声望值较低,我无法提高此回复的评分。 - mpx

0

为了补充 [jedwards答案][1] 的内容:

我使用的是pycharm来实现相同的功能,我需要稍微修改jedwards的建议,以便不是覆盖而是修改 sys.argv。列表 sys.argv[0] 的第一个条目应该保留,因为它包含正在运行的文件的路径。如果不这样做,变量 parser.prog 将出错,因此无法使用。

您可以将文件名附加到 sys.argv 中并扩展列表,而不是手动添加文件名作为列表的第一个条目,如jedwards所做的那样:

if __name__=='__main__':
    # Append additional arguments to sys.argv
    sys.argv = sys.argv + ['--output_dir', 'foo', '1', '2', '3']
    args = parser.parse_args()
    main(args)

这使得它对文件名的更改具有弹性。 [1]: https://dev59.com/bFUL5IYBdhLWcg3wFElR#50886791


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