import argparse
import typing
from typing import Optional
import os
import src.Common.Utils.UserInputHelper as UI
import enum
[docs]class ArgParser:
[docs] class ArgType(enum.Enum):
File = 0
Bool = 1
Options = 2
Enum = 3
String = 4
def __init__(self) -> None:
self.Parser = argparse.ArgumentParser()
self.ArgSettings:typing.Dict[str, typing.Dict[str, object]] = {}
self.ParsedArgs:typing.Dict[str, typing.Tuple[str, bool]] = {}
return
[docs] def SetDefaults(self, defaultsDict) -> None:
args = self._GetArgs()
for key, defaultValue in defaultsDict.items():
if key not in args:
raise Exception(f"Invalid key: {key}")
argSettings = self.ArgSettings[key]
valueStr, validated = args[key]
value = valueStr
if not validated:
value = self._ValidateValue(valueStr, argSettings)
if value is None:
value = defaultValue
self.ParsedArgs[key] = (value, True)
return
def _AddOption(self, name:str, helpStr:str, uiLabel:Optional[str], argType:ArgType) -> None:
self.Parser.add_argument(f"--{name}", type=str, default=None, help=helpStr)
self.ArgSettings[name] = {
"type": argType,
"help": helpStr,
"name": name,
"uiLabel": uiLabel
}
return
[docs] def AddFilePathOption(self, name:str, helpStr:str, folderPath:str, uiLabel:Optional[str]) -> None:
self._AddOption(name, helpStr, uiLabel, ArgParser.ArgType.File)
self.ArgSettings[name]["folderPath"] = folderPath
return
[docs] def AddBoolOption(self, name:str, helpStr:str, uiLabel:Optional[str]) -> None:
self._AddOption(name, helpStr, uiLabel, ArgParser.ArgType.Bool)
return
[docs] def AddOptionsOption(self, name:str, helpStr:str, options:typing.List[str], uiLabel:Optional[str]) -> None:
self._AddOption(name, helpStr, uiLabel, ArgParser.ArgType.Options)
self.ArgSettings[name]["options"] = options
return
[docs] def AddEnumOption(self, name:str, helpStr:str, enumType:enum.Enum, uiLabel:Optional[str]) -> None:
self._AddOption(name, helpStr, uiLabel, ArgParser.ArgType.Enum)
self.ArgSettings[name]["enumType"] = enumType
return
[docs] def AddStrOption(self, name:str, helpStr:str, uiLabel:Optional[str]) -> None:
self._AddOption(name, helpStr, uiLabel, ArgParser.ArgType.String)
return
def _GetArgs(self) -> typing.Dict[str, typing.Tuple[str, bool]]:
if len(self.ParsedArgs) > 0:
return self.ParsedArgs
args = self.Parser.parse_args()
self.ParsedArgs = {}
for argName, argInfo in self.ArgSettings.items():
value = args.__getattribute__(argName)
validated = False
self.ParsedArgs[argName] = (value, validated)
return self.ParsedArgs
[docs] def Get(self, key:str) -> object:
args = self._GetArgs()
if key not in args:
return None
argSettings = self.ArgSettings[key]
valueStr, validated = args[key]
value = valueStr
if not validated:
value = self._ValidateValue(valueStr, argSettings)
if value is None:
value = self._GetValue(argSettings)
self.ParsedArgs[key] = (value, True)
return value
def _ValidateValue(self, value:Optional[str], argInfo:typing.Dict[str, object]) -> Optional[object]:
if value is None:
return None
assert isinstance(value, str), f"value ({value}) must be of type str"
if argInfo["type"] == ArgParser.ArgType.File:
assert isinstance(argInfo["folderPath"], str), "value must be of type str"
# check if it is a file
if not os.path.isfile(value):
print(f"Invalid file path: {value}, for {argInfo['name']}")
return None
# check if it is in the correct folder
filesInFolder = os.listdir(argInfo["folderPath"])
if value not in filesInFolder:
print(f"File should be in {argInfo['folderPath']}, for {argInfo['name']}")
return None
elif argInfo["type"] == ArgParser.ArgType.Bool:
value = value.lower()
if value == "true" or value == "t" or value == "false" or value == "f":
return value == "true" or value == "t"
else:
return None
elif argInfo["type"] == ArgParser.ArgType.Options:
if not isinstance(argInfo["options"], list):
raise Exception("Options should be a list")
if value not in argInfo["options"]:
print(f"Invalid option: {value}, for {argInfo['name']}")
return None
elif argInfo["type"] == ArgParser.ArgType.Enum:
if not isinstance(argInfo["enumType"], enum.Enum):
enumType = argInfo["enumType"]
members = enumType.__members__
for key, member in members.items():
if value == key:
return member
print(f"Invalid option: {value}, for {argInfo['name']}")
return None
return value
def _GetValue(self, argInfo:typing.Dict[str, object]) -> object:
uiLabel = argInfo["uiLabel"]
helpStr = argInfo["help"]
assert isinstance(uiLabel, str), "uiLabel must be of type str"
assert isinstance(helpStr, str), "helpStr must be of type str"
print()
print(helpStr)
if argInfo["type"] == ArgParser.ArgType.File:
assert isinstance(argInfo["folderPath"], str), "folderPath must be of type str"
return UI.FilePicker(uiLabel, argInfo["folderPath"])
elif argInfo["type"] == ArgParser.ArgType.Bool:
return UI.BoolPicker(uiLabel)
elif argInfo["type"] == ArgParser.ArgType.Options:
assert isinstance(argInfo["options"], list), f"options({argInfo}) must be of type str"
return UI.OptionPicker(uiLabel, argInfo["options"])
elif argInfo["type"] == ArgParser.ArgType.Enum:
enumType = argInfo["enumType"]
members = enumType.__members__
key = UI.OptionPicker(uiLabel, list(members.keys()))
return members[key]
elif argInfo["type"] == ArgParser.ArgType.String:
return UI.StrPicker(uiLabel)
return None