2023-11-02 23:28:14 +03:00
|
|
|
#!/usr/bin/env python3
|
2024-05-06 17:47:41 +03:00
|
|
|
from dataclasses import dataclass
|
2023-11-02 23:28:14 +03:00
|
|
|
from typing import *
|
|
|
|
|
|
|
|
|
2024-05-06 17:47:41 +03:00
|
|
|
@dataclass
|
2023-11-02 23:28:14 +03:00
|
|
|
class Option:
|
2024-05-06 17:47:41 +03:00
|
|
|
name: str
|
|
|
|
long: str
|
|
|
|
description: str
|
|
|
|
short: Optional[str] = None
|
|
|
|
value: Optional[str] = None
|
|
|
|
value_default: Optional[str] = None
|
|
|
|
value_parser: Optional[str] = None
|
|
|
|
help: Optional[str] = None
|
|
|
|
conflict: Optional[str] = None
|
|
|
|
append: bool = False
|
|
|
|
cli_only: bool = False
|
|
|
|
deprecated: bool = False
|
|
|
|
experimental: bool = False
|
2023-11-02 23:28:14 +03:00
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
s = "name: " + self.name
|
|
|
|
s += "\nlong: " + self.long
|
|
|
|
if self.short is not None:
|
|
|
|
s += "\nshort: " + self.short
|
|
|
|
if self.value is not None:
|
|
|
|
s += "\nvalue: " + self.value
|
|
|
|
if self.value_default is not None:
|
|
|
|
s += "\nvalue_default: " + self.value_default
|
|
|
|
if self.value_parser is not None:
|
|
|
|
s += "\nvalue_parser: " + self.value_parser
|
|
|
|
if self.help is not None:
|
|
|
|
s += "\nhelp: " + self.help
|
|
|
|
if self.conflict is not None:
|
2024-01-24 10:28:38 +03:00
|
|
|
s += "\nconflict: " + " ".join(self.conflict)
|
2023-11-02 23:28:14 +03:00
|
|
|
if self.append:
|
|
|
|
s += "\nmulti: append"
|
2024-02-29 11:52:54 +03:00
|
|
|
if self.cli_only:
|
|
|
|
s += "\ncli_only: true"
|
2023-11-02 23:28:14 +03:00
|
|
|
if self.deprecated:
|
|
|
|
s += "\ndeprecated: true"
|
2024-03-15 18:45:52 +03:00
|
|
|
if self.experimental:
|
|
|
|
s += "\nexperimental: true"
|
2023-11-02 23:28:14 +03:00
|
|
|
s += "\n---"
|
|
|
|
s += "\n" + self.description
|
|
|
|
return s
|
|
|
|
|
|
|
|
@staticmethod
|
2024-03-15 18:45:52 +03:00
|
|
|
def parse(s) -> "Option":
|
2023-11-02 23:28:14 +03:00
|
|
|
name = None
|
|
|
|
long = None
|
|
|
|
short = None
|
|
|
|
value = None
|
|
|
|
value_default = None
|
|
|
|
value_parser = None
|
|
|
|
help = None
|
|
|
|
conflict = None
|
|
|
|
append = False
|
2024-02-29 11:52:54 +03:00
|
|
|
cli_only = False
|
2023-11-02 23:28:14 +03:00
|
|
|
deprecated = False
|
|
|
|
description = ""
|
2024-03-15 18:45:52 +03:00
|
|
|
experimental = False
|
2023-11-02 23:28:14 +03:00
|
|
|
in_description = False
|
|
|
|
|
|
|
|
for line in s.split("\n"):
|
|
|
|
if line.startswith("---"):
|
|
|
|
in_description = True
|
|
|
|
elif in_description:
|
|
|
|
description += line + "\n"
|
|
|
|
else:
|
|
|
|
key, v = parse_key_value(line)
|
|
|
|
if key == "name":
|
|
|
|
name = v
|
|
|
|
elif key == "long":
|
|
|
|
long = v
|
|
|
|
elif key == "short":
|
|
|
|
short = v
|
|
|
|
elif key == "value":
|
|
|
|
value = v
|
|
|
|
elif key == "value_default":
|
|
|
|
value_default = v
|
|
|
|
elif key == "value_parser":
|
|
|
|
value_parser = v
|
|
|
|
elif key == "help":
|
|
|
|
help = v
|
2024-05-06 17:52:45 +03:00
|
|
|
if help.endswith("."):
|
|
|
|
raise Exception(f"{name}: help should not end with period")
|
2023-11-02 23:28:14 +03:00
|
|
|
elif key == "conflict":
|
2024-01-23 17:19:58 +03:00
|
|
|
conflict = [a.strip() for a in v.split(" ")]
|
2023-11-02 23:28:14 +03:00
|
|
|
elif key == "multi":
|
|
|
|
if v == "append":
|
|
|
|
append = True
|
2024-02-29 11:52:54 +03:00
|
|
|
elif key == "cli_only":
|
|
|
|
if v == "true":
|
|
|
|
cli_only = True
|
|
|
|
elif v == "false":
|
|
|
|
cli_only = False
|
|
|
|
else:
|
2024-05-06 17:55:05 +03:00
|
|
|
raise Exception(
|
|
|
|
f"{name}: Expected true or false for cli attribute"
|
|
|
|
)
|
2023-11-02 23:28:14 +03:00
|
|
|
elif key == "deprecated":
|
|
|
|
if v == "true":
|
|
|
|
deprecated = True
|
|
|
|
elif v == "false":
|
|
|
|
deprecated = False
|
|
|
|
else:
|
|
|
|
raise Exception(
|
2024-05-06 17:52:45 +03:00
|
|
|
f"{name}: Expected true or false for deprecated attribute"
|
2023-11-02 23:28:14 +03:00
|
|
|
)
|
2024-03-15 18:45:52 +03:00
|
|
|
elif key == "experimental":
|
|
|
|
if v == "true":
|
|
|
|
experimental = True
|
|
|
|
elif v == "false":
|
|
|
|
experimental = False
|
|
|
|
else:
|
|
|
|
raise Exception(
|
2024-05-06 17:52:45 +03:00
|
|
|
f"{name}: Expected true or false for experimental attribute"
|
2024-03-15 18:45:52 +03:00
|
|
|
)
|
2023-11-02 23:28:14 +03:00
|
|
|
else:
|
2024-05-06 17:52:45 +03:00
|
|
|
raise Exception(f"{name}: Invalid attribute " + key)
|
2023-11-02 23:28:14 +03:00
|
|
|
|
|
|
|
if name is None:
|
|
|
|
raise Exception("missing name attribute")
|
|
|
|
|
|
|
|
if long is None:
|
2024-05-06 17:52:45 +03:00
|
|
|
raise Exception(f"{name}: missing long attribute")
|
2023-11-02 23:28:14 +03:00
|
|
|
|
|
|
|
return Option(
|
2024-05-06 17:47:41 +03:00
|
|
|
name=name,
|
|
|
|
long=long,
|
|
|
|
short=short,
|
|
|
|
value=value,
|
|
|
|
value_default=value_default,
|
|
|
|
value_parser=value_parser,
|
|
|
|
help=help,
|
|
|
|
conflict=conflict,
|
|
|
|
append=append,
|
|
|
|
cli_only=cli_only,
|
|
|
|
deprecated=deprecated,
|
|
|
|
experimental=experimental,
|
|
|
|
description=description.strip(),
|
2023-11-02 23:28:14 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
2024-03-15 18:45:52 +03:00
|
|
|
def parse_file(filename: str) -> "Option":
|
2023-11-02 23:28:14 +03:00
|
|
|
import sys
|
|
|
|
|
2024-01-24 10:28:38 +03:00
|
|
|
# sys.stderr.write("Parsing " + filename + "\n")
|
2023-11-02 23:28:14 +03:00
|
|
|
s = open(filename).read()
|
|
|
|
return Option.parse(s)
|
|
|
|
|
|
|
|
|
2024-03-15 18:45:52 +03:00
|
|
|
def parse_key_value(s: str) -> tuple[str, str]:
|
2023-11-02 23:28:14 +03:00
|
|
|
if ":" not in s:
|
|
|
|
raise Exception("Expecting key value")
|
|
|
|
index = s.index(":")
|
|
|
|
key = s[:index].strip()
|
|
|
|
value = s[index + 1 :].strip()
|
|
|
|
return key, value
|