如何从 ArgParser 获取参数以写入文本文件,以便可以使用 `fromfile_prefix_chars` 读取它

问题描述 投票:0回答:1

我希望脚本将所有输入参数保存到文本文件中,以便我可以使用该文件使用

fromfile_prefix_chars
重新运行脚本。这是为了方便重现并存储用于生成某些数据的参数。

我的实现非常脆弱且不完整。我确信有更好的方法,但还没有通过谷歌找到它。 ** 问:如何使用 b)干净简洁的代码捕获 a)所有参数?**

背景

ArgParser
具有允许从文本文件读取参数的功能 (
fromfile_prefix_chars
)。重新发明轮子来阅读,例如,似乎没有什么意义。一个 JSON 文件。虽然读取文本文件是
ArgParser
功能集的一部分,但写入该文件似乎不是。我正在寻找一种优雅的解决方案,将
arg: Namespace
parser: ArgumentParser
转换为可以使用
ArgumentParser

读取的文本文件

工作流程示例

  1. 运行脚本
    python script.py --count 7  --channel-stdev 17 /somepath
    这应该会生成一个名为
    args_out.txt
    的文本文件,其内容为
--count
1
--channel-mean
4.5
--channel-stdev
3
/somepath
  1. 我可以使用
    python script.py @args_out.txt
  2. 重新运行脚本

最小示例

from pathlib import Path
import logging
import argparse
from argparse import Namespace, ArgumentParser
from typing import Optional, List, Tuple


def get_arg_parser() -> ArgumentParser:
    parser = ArgumentParser(description='Test',
                            fromfile_prefix_chars='@')
    parser.add_argument('--count', default=1, type=int,
                        help='Number of ')
    parser.add_argument('--channel-mean', default=4.5, type=float,
                        help='Mean number ')
    parser.add_argument('--channel-stdev', default=3, type=float,
                        help='Standard deviation ')

    # Logging control
    log_group = parser.add_mutually_exclusive_group()
    log_group.add_argument('-v', '--debug', action='store_true', help='Enable debug mode')
    log_group.add_argument('-q', '--quiet', action='store_true', help='Enable quiet mode')

    # I/O control
    parser.add_argument('input', nargs='+', help='Path to a single')
    return parser


def parse_args(arg_list: Optional[List[str]] = None) -> Namespace:
    parser = get_arg_parser()
    return parser.parse_args(arg_list)


def convert_args_to_text(args: Namespace, parser: ArgumentParser) -> str:
    """ Convert an argparse.Namespace object to a list of strings that can be written to a file and then
    read as arguments again. """
    required, optionals, optionals_store_true = get_arg_name_to_argument_dicts(parser)

    text = ''
    for k, v in vars(args).items():
        k_dash = k.replace('_', '-')
        if k_dash in optionals:
            text = add_key_and_value(text, key=k_dash, value=v)

        elif k_dash in optionals_store_true:
            if v:
                text = add_key_and_value(text, key=v, value=None)

        elif k_dash in required:
            text = add_key_and_value(text, key=None, value=v)
        else:
            logging.warning(f"skipping argument {k}")

    return text


def get_arg_name_to_argument_dicts(parser: ArgumentParser) -> Tuple[dict, dict, dict]:
    """ Here I try to extract the argument names and whether they are optional or required from the ArgParser
    This is very brittle FIXME """
    optionals = {}
    optionals_store_true = {}  # These are only added if they are true
    required = {}
    for optk, optv in parser._optionals._option_string_actions.items():
        if not optk.startswith('--'):
            # Skip short options
            continue

        if isinstance(optv, argparse._StoreAction):
            optionals[optk.strip('--')] = optv
        elif isinstance(optv, argparse._StoreTrueAction):
            optionals_store_true[optk.strip('--')] = optv

    for req_group_actions in parser._positionals._group_actions:
        name = req_group_actions.dest
        required[name] = req_group_actions

    return required, optionals, optionals_store_true


def add_key_and_value(text: str, key: Optional[str], value: Optional[Tuple[str, List]]) -> str:
    """ Add a argument key and value to a text block. """

    if key is not None:
        text += f"--{key}\n"

    if value is None:
        return text

    if isinstance(value, list):
        for vi in value:
            text += f'{vi}\n'
    else:
        text += f'{value}\n'

    return text


def main():
    args = parse_args()

    print(args)

    text = convert_args_to_text(args, get_arg_parser())
    with open('args_out.txt', 'w') as f:
        f.write(text)


if __name__ == '__main__':
    main()

谢谢您的帮助

python python-3.x argparse
1个回答
0
投票

我发现你的方法令人困惑。考虑:

import arg
import json
import argparse
import shlex
from pathlib import Path


def dump_args():
   with Path("args_out.txt", "w") as f:
      print(shlex.join(sys.argv[1:]), file=f)

parser = argparse.ArgumentParser....
...
dump_args()

然后您可以使用 shell 命令

xargs
重新运行脚本,该命令也会解析引号。

xargs ./script.py <args_out.txt

您还可以使用以下命令从 python 重新运行脚本:

with Path("args_out.txt") as f:
    args = parser.parse_args(shlex.split(f.read()))
© www.soinside.com 2019 - 2024. All rights reserved.