Closed pbarker closed 2 years ago
Found a way to do this, and in case anyone needs it in the future:
import logging
import os
from datetime import datetime
from logging import Logger
from typing import Dict, List, Tuple, Any
from simple_parsing import ArgumentParser
def hyperparameters_from_parser(parser: ArgumentParser, ns_map: Dict[str, object]) -> Dict[str, Any]:
"""Generate Sagemaker style hyperparameters from a parser. This exists because the simple_parser
will do conflict resolution across dataclasses and change flag names. Sagemaker then needs a
dictionary of those new flag names
Args:
parser (ArgumentParser): parser to use, should not have called parse_args() at this time
ns_map (Dict[str, object]): a map of parser namespace to corresponding object e.g. {'train': MyTrainArgsDataclass}
Returns:
Dict[str, Any]: A map of arguments to their values
"""
# get the final argument names
parser._resolve_conflicts()
# dynamically create a variable by the same name as the argparse namespace, set equal to the corresponding object
for ns, obj in ns_map.items():
# check that the namespace exists in the parser
ns_exists = False
for wrapper in parser._wrappers:
if wrapper.name == ns:
ns_exists = True
break
if not ns_exists:
raise ValueError(f"namespace: {ns} - does not exist in parser")
globals()[ns] = obj
hyperparameters = {}
for wrapper in parser._wrappers:
for f in wrapper.fields:
# get the command line argument and clean
arg = f.option_strings[0]
plain_arg = arg.lstrip("--")
# from the dynamically create variable above, access its properties dynamically and pull out the value
# which corresponds to the command line argument
dest = f.arg_options["dest"]
e = f"val = {dest}"
l = locals()
exec(e, globals(), l)
val = l["val"]
hyperparameters[plain_arg] = val
return hyperparameters
parser = ArgumentParser()
parser.add_arguments(SagemakerArgs, dest="sagemaker")
parser.add_arguments(TrainArgs, dest="train")
parser.add_arguments(EvaluationArgs, dest="eval")
sm_args: SagemakerArgs = args.sagemaker
train_args: TrainArgs = args.train
eval_args: EvaluationArgs = args.eval
hyperparameters = hyperparameters_from_parser(
parser, {"sagemaker": sm_args, "train": train_args, "eval": eval_args}
)
Is your feature request related to a problem? Please describe. A way to get flag values once they are merged. This comes from using Amazon Sagemaker where they want CLI arguments formatted as a dictionary, which they then apply to the container.
Describe the solution you'd like
Describe alternatives you've considered I've been digging through the code and don't see an easy way of doing this today but please let me know if I missed something!