Skip to content

Commit

Permalink
fix: handle nested arguments in the argument parser
Browse files Browse the repository at this point in the history
Mostly coming from libraries such as simple-parsing, who nest the
values, but this can also be done manually, so might as well account for
it.
Also this makes sure the values are serialisable, as JSON is very
restrictive and Python doesn't allow to handle it gracefully (as in
having the possibility to define how an object is serialised).
  • Loading branch information
jungomi committed Jul 12, 2023
1 parent 7994c1b commit d6c20f8
Showing 1 changed file with 40 additions and 8 deletions.
48 changes: 40 additions & 8 deletions py/lavd/log.py
Expand Up @@ -1070,18 +1070,23 @@ def assign_args_to_options(
) -> Dict[str, Union[List, Dict]]:
pos = []
opts = {}
for key, value in vars(args).items():
for key, o in options.items():
# None values are not saved, they just cause trouble because they are null and
# undefined, and they should rather not be present than being null.
if o is None:
continue
value = get_recursive_option(args, key)
if value is None:
continue
if key in positionals:
pos.append(value)
else:
o = options.get(key)
if o is not None:
name = o.get("name") or key
opts[name] = value
name = o.get("name") or key
opts[name] = ensure_serialisable(value)
for key, o in positionals.items():
if o is None:
continue
value = get_recursive_option(args, key)
if value is None:
continue
pos.append(ensure_serialisable(value))
out: Dict[str, Union[List, Dict]] = {}
if len(pos) > 0:
out["positional"] = pos
Expand All @@ -1090,6 +1095,33 @@ def assign_args_to_options(
return out


# Convert everything that is not serialisable by the JSON module to strings to make them
# serialisable.
def ensure_serialisable(value: Any) -> Any:
if isinstance(value, dict):
return {k: ensure_serialisable(v) for k, v in value.items()}
elif isinstance(value, (list, tuple)):
return [ensure_serialisable(v) for v in value]
elif not isinstance(value, (str, int, float, bool)):
return str(value)
else:
return value


def get_recursive_option(options: Any, key: str) -> Optional[Any]:
curr = options
for k in key.split("."):
if not isinstance(options, dict):
try:
curr = vars(curr)
except TypeError:
raise KeyError(f"Could not get key={key} from {curr}")
curr = curr.get(k)
if curr is None:
return None
return curr


def pad_table_row(row: List[str], widths: List[int], value: str = " ") -> List[str]:
return [
"{field:{pad}<{width}}".format(field=field, pad=value, width=width)
Expand Down

0 comments on commit d6c20f8

Please sign in to comment.