0

I derive some settings from my parser that I latter store on disk to be reloaded again, during the reload I want to overwrite some values but keep the first ones, similar to new default values.

I want to have a priority of args2 > args1 > default values, however I face the challenge on how to determine the highest priority, especially when I pass the default value explicitly in arg2, it should not fall back to args1, i.e. a simple comparison with the default is not sufficient.

args1 = ["-a", "1", "--bar", "2"]  # Run 1
args2 = ["--bar", "default-bar", "-c", "3"]  # Run 2, override bar with default
args2_b = ["-c", "3"]  # Run 2b, bar should be restored from Run 1

# Equivalent full args for Run 2
wanted = ["-a", "1", "--bar", "default-bar", "-c", "3"]
wanted_b = ["-a", "1", "--bar", "2", "-c", "3"]

from argparse import ArgumentParser

parser = ArgumentParser()
# required arguments without defaults can be present too
parser.add_argument("--foo", "-a", default="default-foo")
parser.add_argument("--bar", "-b", default="default-bar")
parser.add_argument("--baz", "-c", default="default-baz")

results1 = parser.parse_args(args1)
print(results1) # Namespace(foo='1', bar='2', baz='default-baz')
results2 = parser.parse_args(args1)
print(results2) # Namespace(foo='default-foo', bar='default-bar', baz='2')

# merge results
# ...?

I do not want to make things too complicated and hard to maintain, therefore manually looking at sys.argv does not look feasible (I have many arguments, shorthand codes, usage of dest, ...).
I use tap Typed Argument Parser - if that info helps to find a better solution, also I would like to keep most of tap's features, i.e. to define most arguments in a class and keep meaningful default values there.


TL;DR: how could I differentiate between these two cases in Run 2?

# Run 1
file.py -a 1 --bar 2
# Run 2, that references some state from Run 1
# Use passed value:
file.py -c 3 --restore-run 1  --bar "default-bar"

# Use value from Run 1
file.py -c 3 --restore-run 1

Related questions:

2
  • I don't know what the question is. Let's start with run 2 arguments and what you want results2 to be. Commented Sep 29 at 21:06
  • I have many arguments and many with defaults. I want to take the arguments of run 1 as the base - without the need to remember or rewrite them to avoid mistakes. The problem is when is when run 1 has a non-default argument. For run 2, I want to explicitly change it back to the default value. For run 2b, I want to keep the restored value of run 1. My question is how do I differentiate these two cases. I cannot simply do a if args_in_run2.bar == default_value: use value from run1; as I do not know if args_in_run2.bar is implicit or explicitly the default value. Commented Oct 1 at 8:40

1 Answer 1

0

First I'll provide a more general answer, that reveals which actions were actually parsed by user arguments:

import argparse
from copy import deepcopy


parser = argparse.ArgumentParser()
# required arguments without defaults can be present too
parser.add_argument("--foo", "-a", default="default-foo")
parser.add_argument("--bar", "-b", default="default-bar")
parser.add_argument("--baz", "-c", default="default-baz")


new_parser = deepcopy(parser)
for action in new_parser ._actions:
    action.default = argparse.SUPPRESS

# get args that are explicitly passed in CLI:
cli_args = new_parser.parse_args()
# Namespace()

print(new_parser.parse_args(["--bar", "default-bar", "-c", "3"]))
# Namespace(bar='default-bar', baz='3')
print(new_parser.parse_args(["-c", "2"]))
# Namespace(baz='2')

# Use parser to get the normally parsed args and use cli_args in whatever way necessary

In my case I do it similarly, but I construct an updated new parser without using argparse.SUPPRESS instead I use the restored values as new default values:

import argparse
from copy import deepcopy

parser = argparse.ArgumentParser()
# required arguments without defaults can be present too
parser.add_argument("--foo", "-a", default="default-foo")
parser.add_argument("--bar", "-b", default="default-bar")
parser.add_argument("--baz", "-c", default="default-baz")

# parse args and detect I want to restore:
from types import SimpleNamespace
restored_namespace = SimpleNamespace(foo=1, bar=2)

new_parser = deepcopy(parser)

# update defaults or new_parser with restored values
actions: list[argparse.Action] = new_parser ._actions
for action in actions:
    action.default = getattr(restored_namespace, action.dest, action.default)  # set new default values

# now CLI args2 > restored args 1> defaults (during args1)
final_args = new_parser.parse_args()

print(new_parser.parse_args(["--bar", "default-bar", "-c", "3"]))
# Namespace(foo=1, bar='default-bar', baz='3')
print(new_parser.parse_args(["-c", "2"]))
# Namespace(foo=1, bar=2, baz='2')
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.