Python Argparse,如何正确组织 ArgParse 代码

Posted

技术标签:

【中文标题】Python Argparse,如何正确组织 ArgParse 代码【英文标题】:Python Argparse, How to Properly Organize ArgParse Code 【发布时间】:2017-12-11 23:21:20 【问题描述】:

我已经有 10 年没有使用 argparse 了,但我理解它并且我下面的内容确实可以按照我的意愿工作......但是随着我继续添加命令和解析器,这将变得更加复杂和子解析器。我想知道组织这个的最好方法是什么?在我看来,我应该能够像在图表中看到的一样清楚地看到文本中的命令序列……但是每次我看它时,离开一段时间后,我的大脑会在我试图跟随时游动它。一定有更好的方式来组织这个吧?

import argparse
from modules import cli_tools

#LVL 1: create the top-level parser for the "sacs" command.
sacs_parser = argparse.ArgumentParser(prog = 'sacs', description = 'Master Command For Controlling SACS.')
sacs_subparsers = sacs_parser.add_subparsers(help='Management Module Selector.')

#LVL 2: create the second-level parsers for the "sacs [module]" commands.
csv_parser = sacs_subparsers.add_parser('csv', help='Generic CSV Management Module.')
am_parser = sacs_subparsers.add_parser('am', help='SACS Asset Management Module.')
mm_parser = sacs_subparsers.add_parser('mm', help='SACS Metric Management Module.')

#LVL 3: create the third-level subparser for the "sacs [module] [action]" commands.
csv_action_subparser = csv_parser.add_subparsers(help='The action to perform.')
mm_action_subparser = mm_parser.add_subparsers(help='The action to perform.')

#LVL 4: create the fourth-level subparser for the "sacs [module] [action] [type]" commands.
mm_create_parser = mm_action_subparser.add_parser('create', help='Used to Create a new event/asset input file.')
mm_create_type_parser = mm_create_parser.add_subparsers(help='The type of file to create.')

#LVL 5: create the fifth-level parser for the "sacs [module] [action] [type]" commands.
csv_reconcile_parser = csv_action_subparser.add_parser('reconcile', help='reconcile two csvs.')
mm_create_asset_parser = mm_create_type_parser.add_parser('assets', help='Create an Asset File.')
mm_create_asset_subtype_parser = mm_create_asset_parser.add_subparsers(help='The type of file to create.')
mm_create_event_parser = mm_create_type_parser.add_parser('events', help='Create an Event File.')

#LVL 6: create the sixth-level parser for the "sacs [module] [action] [type] [subtype]" commands.
mm_create_asset_uaid_parser = mm_create_asset_subtype_parser.add_parser('uaid', help='Create an Asset File with UAID as the primary key.')
mm_create_asset_vid_parser = mm_create_asset_subtype_parser.add_parser('vid', help='Create an Asset File with Vulnerability ID as the primary key.')

#COMMAND ARGS: Add Arguments to the final command "sacs csv reconcile [args]"
csv_reconcile_parser.add_argument('key', help='The name of the field that holds the unique ID to compare against.')
csv_reconcile_parser.add_argument('inputfile1', help='The master file (used when same record exists in both files).')
csv_reconcile_parser.add_argument('inputfile2', help='The secondary file, which is trumped by the master file.')
csv_reconcile_parser.add_argument('outputfile', help='The output file; note it will be overwritten if it exists.')
csv_reconcile_parser.set_defaults(func=cli_tools.csv_reconcile)

#COMMAND ARGS: Add Arguments to the final command "sacs mm create assets uaid [args]"
mm_create_asset_uaid_parser.add_argument('appmapp_file', help='The input file.')
mm_create_asset_uaid_parser.add_argument('output_file', help='The output file.')
mm_create_asset_uaid_parser.set_defaults(func=cli_tools.asset_create_uaid)

#COMMAND ARGS: Add Arguments to the final command "sacs mm create assets vid [args]"
mm_create_asset_vid_parser.add_argument('vulnerability_file', help='The input file.')
mm_create_asset_vid_parser.add_argument('appmapp_file', help='The output file.')
mm_create_asset_vid_parser.add_argument('output_file', help='The output file.')
mm_create_asset_vid_parser.set_defaults(func=cli_tools.asset_create_vid)

args = sacs_parser.parse_args()
args.func(args)

改善方法的潜在途径:

    解析器/子解析器重命名。 更改语句的顺序。 某种方式来缩进而不弄乱 python。

所有想法都摆在桌面上,我想看看其他人在设计复杂命令时如何处理。

【问题讨论】:

我会更改排序并使用较短的变量名称。解析器一创建就添加参数,并为解析器使用一个简短的通用名称。然后,您可以为每个子解析器重用相同的短名称。 【参考方案1】:

增加 Python 代码清晰度的常用方法是将步骤封装在函数甚至类中。 argparse 本身就是一组类。您的每个解析器(包括子解析器)都是 argparse.ArgumentParser 对象。每个add_argument 创建一个argparse.Action 子类对象。 add_subparsers 创建(并返回)一个专门处理子解析器的Action 子类。最后parse_args 返回一个argparse.Namespace 对象。

我不知道这是否适用于您的情况。你的代码是可读的,所以我很容易知道你在做什么。我从未见过有人使用这么多级别的子解析器。事实上,使用多个级别是一种新奇事物(从早期的一些 SO 问题来看)。

我想强调argparse 的第一项工作是弄清楚您的用户想要什么。解析是第一要务。其次,它应该使表达他们想要的东西变得容易。我想知道这种多级子解析是否易于使用。 argparse 在子解析器之间拆分 help 的方式使得很难显示一个大的概述。

在某些时候,大包开始编写自己的帮助,甚至自定义解析器以满足他们的需求。

您可能会在CodeReview 上得到更好的答案。那里的常客似乎对代码组织和命名问题更感兴趣。 SO 更倾向于解决问题。如果主题过于专业,我不鼓励推荐给 CR,但这更像是一个普通的 Python 问题,而不是argparse 特定的问题。但请阅读 CR 对问题的期望。

Multiple level argparse subparsers

Argparse with required subparser

argparse subparser monolithic help output

Python argparser repeat subparse

【讨论】:

感谢您的信息...将有助于了解您如何重组它...我绝对喜欢封装在函数中,但不确定使用此包的最佳方法。至于制作这样的多 lvl 命令,我在设计这个时有点考虑 zfs 和 zpool 命令...... argparse 似乎使事情变得比它需要的更复杂,自定义解析器会很棒!唯一的一点,我真的不知道如何将 main-command/python-file-name 之后的所有内容放入一个变量中,以便我可以自定义解析它。

以上是关于Python Argparse,如何正确组织 ArgParse 代码的主要内容,如果未能解决你的问题,请参考以下文章

Python argparse required=True 但 --version 功能?

Python argparse 忽略无法识别的参数

具有 nargs 行为的 Python argparse 不正确

python之定义参数模块argparse的基本使用

如何将 argparse 参数传递给类

将 Python argparse.Namespace() 视为字典的正确方法是啥?