llama.cpp/tools/server/public_simplechat/local.tools/config.py

163 lines
5.3 KiB
Python

# Config entries
# by Humans for All
#
from dataclasses import dataclass, field, fields
from typing import Any, Optional
import http.server
import ssl
import sys
import urlvalidator as mUV
import debug as mDebug
gConfigNeeded = [ 'acl.schemes', 'acl.domains', 'sec.bearerAuth' ]
@dataclass
class DictDataclassMixin(dict):
"""
Mixin to ensure dataclass attributes get mapped as dict keys in __post_init__
"""
def __post_init__(self):
"""
Skip if already set using maybe say dict init mechanism or ...
"""
for f in fields(self):
if f.name not in self:
self[f.name] = getattr(self, f.name)
@dataclass
class Sec(DictDataclassMixin, dict):
"""
Used to store security related config entries
"""
certFile: str = ""
keyFile: str = ""
bearerAuth: str = ""
@dataclass
class ACL(DictDataclassMixin, dict):
schemes: list[str] = field(default_factory=list)
domains: list[str] = field(default_factory=list)
@dataclass
class Network(DictDataclassMixin, dict):
port: int = 3128
addr: str = ''
@dataclass
class Op(DictDataclassMixin, dict):
configFile: str = "/dev/null"
debug: bool = False
server: http.server.ThreadingHTTPServer|None = None
sslContext: ssl.SSLContext|None = None
bearerTransformed: str = ""
bearerTransformedYear: str = ""
@dataclass
class Config(DictDataclassMixin, dict):
op: Op = field(default_factory=Op)
sec: Sec = field(default_factory=Sec)
acl: ACL = field(default_factory=ACL)
nw: Network = field(default_factory=Network)
def get_type(self, keyTree: str):
cKeyList = keyTree.split('.')
cur = self
for k in cKeyList[:-1]:
cur = self[k]
return type(cur[cKeyList[-1]])
def get_value(self, keyTree: str):
cKeyList = keyTree.split('.')
cur = self
for k in cKeyList[:-1]:
cur = self[k]
return cur[cKeyList[-1]]
def set_value(self, keyTree: str, value: Any):
cKeyList = keyTree.split('.')
cur = self
for k in cKeyList[:-1]:
cur = self[k]
cur[cKeyList[-1]] = value
def validate(self):
for k in gConfigNeeded:
if self.get_value(k) == None:
print(f"ERRR:ProcessArgs:Missing:{k}:did you forget to pass the config file...")
exit(104)
mDebug.setup(self.op.debug)
if (self.acl.schemes and self.acl.domains):
mUV.validator_setup(self.acl.schemes, self.acl.domains)
def load_config(self, configFile: str):
"""
Allow loading of a json based config file
The config entries should be named same as their equivalent cmdline argument
entries but without the -- prefix.
As far as the logic is concerned the entries could either come from cmdline
or from a json based config file.
"""
import json
self.op.configFile = configFile
with open(self.op.configFile) as f:
cfgs: dict[str, Any] = json.load(f)
for cfg in cfgs:
print(f"DBUG:LoadConfig:{cfg}")
try:
neededType = self.get_type(cfg)
gotValue = cfgs[cfg]
gotType = type(gotValue)
if gotType.__name__ != neededType.__name__:
print(f"ERRR:LoadConfig:{cfg}:expected type [{neededType}] got type [{gotType}]")
exit(112)
self.set_value(cfg, gotValue)
except KeyError:
print(f"ERRR:LoadConfig:{cfg}:UnknownConfig!")
exit(113)
def process_args(self, args: list[str]):
"""
Helper to process command line arguments.
Flow setup below such that
* location of --config in commandline will decide whether command line or config file will get
priority wrt setting program parameters.
* str type values in cmdline are picked up directly, without running them through ast.literal_eval,
bcas otherwise one will have to ensure throught the cmdline arg mechanism that string quote is
retained for literal_eval
"""
import ast
print(self)
iArg = 1
while iArg < len(args):
cArg = args[iArg]
if (not cArg.startswith("--")):
print(f"ERRR:ProcessArgs:{iArg}:{cArg}:MalformedCommandOr???")
exit(101)
cArg = cArg[2:]
print(f"DBUG:ProcessArgs:{iArg}:{cArg}")
try:
aTypeCheck = self.get_type(cArg)
aValue = args[iArg+1]
if aTypeCheck.__name__ != 'str':
aValue = ast.literal_eval(aValue)
aType = type(aValue)
if aType.__name__ != aTypeCheck.__name__:
print(f"ERRR:ProcessArgs:{iArg}:{cArg}:expected type [{aTypeCheck}] got type [{aType}]")
exit(102)
self.set_value(cArg, aValue)
iArg += 2
if cArg == 'op.configFile':
self.load_config(aValue)
except KeyError:
print(f"ERRR:ProcessArgs:{iArg}:{cArg}:UnknownArgCommand!:{sys.exception()}")
exit(103)
print(self)
self.validate()