0

I'm trying to create a terminal application a bit similar to cutechess-cli, which has options like the following:

-epdout FILE    Save the end position of the games to FILE in FEN format.
-recover        Restart crashed engines instead of stopping the match
-repeat [N]     Play each opening twice (or N times). Unless the -noswap...

which can all be done with argparse in Python.

However it also has "named arguments" like the following:

-resign movecount=COUNT score=SCORE [twosided=VALUE]
        Adjudicate the game as a loss if an engine's score is
        at least SCORE centipawns below zero for at least COUNT
        consecutive moves.
-sprt elo0=ELO0 elo1=ELO1 alpha=ALPHA beta=BETA
        Use a Sequential Probability Ratio Test as a termination
        criterion for the match. This option should only be used...

I can implement that with argparse using nargs='*' and then writing my own parser (maybe just regex). However that doesn't give nice documentation, and if argparse can already do something like this, I would rarther use the builtin approach.

Summary: Does argparse have a concept of named arguments similar to resign and sprt above? And if not, would the best approach be to do this manyally using nargs='*'?

Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
  • 1
    If I understand your problem correctly, I think you are looking for a [sub-parser](https://docs.python.org/3/library/argparse.html#sub-commands) – Tomerikoo Sep 10 '19 at 12:59
  • `nargs=3`, and after parsing unpack the list value: `COUNT, SCORE, VALUE = args.resign`. And write the `help` line as shown. – hpaulj Sep 10 '19 at 14:47
  • While you can do fancy things with `type` and `action`, it's often simpler to fiddle with the values after parsing. – hpaulj Sep 10 '19 at 14:50
  • @Tomerikoo That would give me `cmd.py resign -movecount 40 -score 500 sprt -elo 100 -elo1 200`. I guess that's somehow similar, but I feel subparsers are more commonly used for separate programs, like `git rebase` and `git stash` etc. – Thomas Ahle Sep 10 '19 at 18:08
  • @hpaulj That would work, but then the arguments wouldn't be named. In the documentation that wouldn't matter, but in a long command line, it can be nice to have names. – Thomas Ahle Sep 10 '19 at 18:09
  • There have been previous requests for arbitrary `key=value` pairs, with similar advise - parse them yourself. https://stackoverflow.com/q/45025414/901925 – hpaulj Sep 10 '19 at 22:04

1 Answers1

1

You can use a custom type to split the values and use the metavar argument to give a better description for the value:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--arg', nargs='*', type=lambda text: text.split('=', maxsplit=1), metavar='PARAM-NAME=PARAM-VALUE', help='Some other parameters')
args = parser.parse_args()
args.arg = {k: v for k,v in args.arg}

Which produces:

usage: [-h] [--arg [PARAM-NAME=PARAM-VALUE [PARAM-NAME=PARAM-VALUE ...]]]

optional arguments:
  -h, --help            show this help message and exit
  --arg [PARAM-NAME=PARAM-VALUE [PARAM-NAME=PARAM-VALUE ...]]
                        Some other parameters

If you wanted to you could avoid the "postprocessing" step to build the dictionary by using a custom Action type. But it seems an overkill to me in this case.

Giacomo Alzetta
  • 2,431
  • 6
  • 17
  • That's a really neat solution! Would making my own `Action` type allow me to rewrite the metavars to show multiple optional values? Like `A=X [B=Y] [C=Z]`. – Thomas Ahle Sep 10 '19 at 14:03