4 from .lcnodes import LCModuleNode
15 self.__file = env.to_wspath(file)
18 tree = ast.parse(f.read(), self.__file, mode='exec')
19 self.__module = LCModuleNode(self, tree)
27 def root_module(self):
30 def compile_astns(self, astns):
31 if not isinstance(astns, list):
35 ast.Module(body=astns, type_ignores=[]),
36 self.__file, mode='exec')
38 class DependencyGraph:
39 def __init__(self) -> None:
42 def add(self, dependent, dependee):
43 if dependent in self._edges:
44 if dependee in self._edges[dependent]:
46 self._edges[dependent].add(dependee)
48 self._edges[dependent] = set([dependee])
50 if self.__check_loop(dependee):
51 raise ConfigLoadException(
52 f"loop dependency detected: {dependent.get_name()}",
55 def __check_loop(self, start):
60 if current in visited:
64 if current not in self._edges:
66 for x in self._edges[current]:
71 def cascade(self, start):
75 if current in self._edges:
76 for x in self._edges[current]:
80 class ConfigTypeFactory:
81 def __init__(self) -> None:
84 def regitser(self, provider_type):
85 self.__providers.append(provider_type)
87 def create(self, typedef):
88 for provider in self.__providers:
89 if not provider.typedef_matched(typedef):
91 return provider(typedef)
93 raise ConfigLoadException(
94 f"no type provider defined for type: {typedef}", None)
96 class LConfigEvaluationWrapper:
97 def __init__(self, env, node) -> None:
102 self.__env.push_executing_node(self.__node)
105 def __exit__(self, type, value, tb):
106 self.__env.pop_executing_node()
109 return self.__env.evaluate_node(self.__node)
111 class LConfigEnvironment(Renderable):
112 def __init__(self, workspace, config_io) -> None:
115 self.__ws_path = path.abspath(workspace)
116 self.__exec_globl = globals()
117 self.__eval_stack = []
118 self.__lc_modules = []
119 self.__config_val = {}
120 self.__node_table = {}
121 self.__deps_graph = DependencyGraph()
122 self.__type_fatry = ConfigTypeFactory()
123 self.__config_io = config_io
125 def to_wspath(self, _path):
126 _path = path.abspath(_path)
127 return path.relpath(_path, self.__ws_path)
129 def register_builtin_func(self, func_obj):
130 call = (lambda *arg, **kwargs:
131 func_obj(self, *arg, **kwargs))
133 self.__exec_globl[func_obj.name] = call
135 def resolve_module(self, file):
136 fo = LConfigFile(self, file)
137 self.__lc_modules.insert(0, (fo.root_module()))
139 def evaluate_node(self, node):
140 name = node.get_name()
142 return self.get_symbol(name)()
144 def eval_context(self, node):
145 return LConfigEvaluationWrapper(self, node)
147 def push_executing_node(self, node):
148 self.__eval_stack.append(node)
150 def pop_executing_node(self):
151 return self.__eval_stack.pop()
153 def register_node(self, node):
154 self.__node_table[node.get_name()] = node
158 self.push_executing_node(node)
159 exec(node.get_co(), self.__exec_globl, l)
160 self.pop_executing_node()
161 except Exception as e:
162 raise ConfigLoadException("failed to load", node, e)
164 self.__exec_globl.update(l)
166 def lookup_node(self, name):
167 if name not in self.__node_table:
168 raise ConfigLoadException(f"node '{name}' undefined")
169 return self.__node_table[name]
171 def type_factory(self):
172 return self.__type_fatry
174 def update_value(self, key, value):
175 self.__config_val[key] = value
177 def get_symbol(self, name):
178 if name not in self.__exec_globl:
179 raise ConfigLoadException(f"unknown symbol '{name}'")
180 return self.__exec_globl[name]
182 def callframe_at(self, traverse_level):
184 return self.__eval_stack[traverse_level - 1]
188 def lookup_value(self, key):
189 return self.__config_val[key]
191 def dependency(self):
192 return self.__deps_graph
195 for mod in self.__lc_modules:
199 self.__config_io.export(self, self.__config_val)
201 def save(self, _path = ".config.json"):
203 for mod in self.__lc_modules:
206 with open(_path, 'w') as f:
209 def load(self, _path = ".config.json"):
210 if not path.exists(_path):
214 with open(_path, 'r') as f:
215 data.update(json.load(f))
217 for mod in self.__lc_modules:
218 mod.deserialise(data)
222 def render(self, rctx):
223 for mod in self.__lc_modules: