162 lines
7.0 KiB
Python
162 lines
7.0 KiB
Python
import configparser
|
||
import json
|
||
import copy
|
||
import math
|
||
|
||
class ConfParse:
|
||
def __init__(self, config_def_json_files_paths):
|
||
self.config_def_json_files_paths = config_def_json_files_paths
|
||
self.configs = {}
|
||
for file in self.config_def_json_files_paths:
|
||
with open(file, 'r') as f:
|
||
jf = json.load(f)
|
||
if "settings" in jf:
|
||
setting_json = jf["settings"]
|
||
for key, value in setting_json.items():
|
||
# print(key, value)
|
||
all_items = self._expend_children(value["children"])
|
||
for item_key, item_value in all_items.items():
|
||
self.configs[item_key] = item_value
|
||
elif "overrides" in jf:
|
||
override_json = jf["overrides"]
|
||
for key, value in override_json.items():
|
||
if key in self.configs:
|
||
for item_key, item_value in value.items():
|
||
self.configs[key][item_key] = item_value
|
||
else:
|
||
self.configs[key] = value
|
||
|
||
def _expend_children(self, config):
|
||
tmp_c = {}
|
||
for key,val in config.items():
|
||
if "children" in val:
|
||
tmp_c.update(self._expend_children(val["children"]))
|
||
val.pop("children", None)
|
||
tmp_c[key] = val
|
||
return tmp_c
|
||
|
||
def add_inst_cfg(self, inst_cfg_files_paths):
|
||
copy_settings = copy.deepcopy(self.configs)
|
||
config = configparser.ConfigParser()
|
||
for file in inst_cfg_files_paths:
|
||
config.read(file)
|
||
if config.has_section('values'):
|
||
for key, val in config.items('values'):
|
||
v = str(val)
|
||
if key not in copy_settings:
|
||
copy_settings[key] = {}
|
||
if v.startswith("="):
|
||
copy_settings[key]["value"] = v[1:]
|
||
else:
|
||
copy_settings[key]["value"] = v
|
||
return copy_settings
|
||
|
||
|
||
def parse_configs(self, settings):
|
||
class ConfigStr(str):
|
||
def __mul__(self, other): raise TypeError()
|
||
def __rmul__(self, other): raise TypeError()
|
||
def __add__(self, other): raise TypeError()
|
||
def __radd__(self, other): raise TypeError()
|
||
def __sub__(self, other): raise TypeError()
|
||
def __rsub__(self, other): raise TypeError()
|
||
def __truediv__(self, other): raise TypeError()
|
||
def __rtruediv__(self, other): raise TypeError()
|
||
def __pow__(self, other): raise TypeError()
|
||
def __rpow__(self, other): raise TypeError()
|
||
|
||
parsed_settings = copy.deepcopy(settings)
|
||
|
||
last_unparsed = -1
|
||
while True:
|
||
unparsed = 0
|
||
|
||
# 构建上下文环境变量上下文,用作eval的变量替换
|
||
context = {}
|
||
for k, v in parsed_settings.items():
|
||
# 上下文里的变量取值:优先使用 value(如果有),否则使用 default_value
|
||
if "value" in v:
|
||
val = v["value"]
|
||
elif "default_value" in v:
|
||
val = v["default_value"]
|
||
else:
|
||
val = None
|
||
|
||
if isinstance(val, str):
|
||
if val.lower() == "true":
|
||
val = True
|
||
elif val.lower() == "false":
|
||
val = False
|
||
else:
|
||
try:
|
||
val = int(val)
|
||
except ValueError:
|
||
try:
|
||
val = float(val)
|
||
except ValueError:
|
||
val = ConfigStr(val)
|
||
context[k] = val
|
||
|
||
# 自定义函数实现
|
||
def resolveOrValue(key):
|
||
return context.get(key)
|
||
|
||
def extruderValues(key):
|
||
# 兼容简易的多挤出机查询逻辑,目前单挤出机环境下返回一个单元素列表
|
||
return [context.get(key)]
|
||
|
||
def extruderValue(extruder_position, key):
|
||
# 对于单挤出机环境或者全局配置,直接忽略 extruder_position,返回指定的 key 对应的值
|
||
return context.get(key)
|
||
|
||
def defaultExtruderPosition():
|
||
return 0
|
||
|
||
# 提供基础的运算函数支持
|
||
builtin_funcs = {
|
||
"max": max,
|
||
"min": min,
|
||
"abs": abs,
|
||
"round": round,
|
||
"int": int,
|
||
"float": float,
|
||
"bool": bool,
|
||
"math": math,
|
||
"resolveOrValue": resolveOrValue,
|
||
"extruderValues": extruderValues,
|
||
"extruderValue": extruderValue,
|
||
"defaultExtruderPosition": defaultExtruderPosition
|
||
}
|
||
|
||
for key, val_dict in parsed_settings.items():
|
||
for field, field_val in val_dict.items():
|
||
# 仅对字符串进行尝试计算
|
||
if "type" == field:
|
||
continue
|
||
|
||
if isinstance(field_val, str):
|
||
try:
|
||
# 如果是一个普通的纯字符串,比如分类名(如"Machine Type" ),eval可能会抛出SyntaxError或NameError
|
||
# 如果它是一个python表达式,则会被顺利计算出结果
|
||
evaluated = eval(field_val, {"__builtins__": builtin_funcs}, context)
|
||
# 避免将原本只是用来作类型声明的 "int"/"float" 纯字符串,被错误求值为内置 Python type class 打断 JSON 序列化
|
||
if evaluated != field_val and not isinstance(evaluated, type):
|
||
if val_dict.get("type") == "str" and not isinstance(evaluated, str):
|
||
if isinstance(evaluated, (list, dict)):
|
||
val_dict[field] = json.dumps(evaluated).replace(" ", "")
|
||
else:
|
||
val_dict[field] = str(evaluated)
|
||
else:
|
||
val_dict[field] = evaluated
|
||
except Exception:
|
||
# 解析失败(例如是个普通英文字符串或者依赖还没被解开)则暂不做处理
|
||
unparsed += 1
|
||
|
||
# 如果在这一轮中未解析出的表达式数量不再减少,说明已经到达极限,跳出循环
|
||
if unparsed == last_unparsed:
|
||
break
|
||
last_unparsed = unparsed
|
||
|
||
return parsed_settings
|
||
|
||
|