13 from abc import abstractmethod
17 class LCNode(Renderable):
18 def __init__(self, fo, astn):
23 self._astn = self.prune_astn(astn)
24 self._co = self.compile_astn()
27 self._env.register_node(self)
29 def prune_astn(self, astn):
32 def compile_astn(self):
33 return self._fo.compile_astns(self._astn)
41 def set_parent(self, new_parent):
42 self._parent = new_parent
47 def add_child(self, node):
50 def remove_child(self, node):
54 return f"LCNode: {self.__hash__()}"
60 def render(self, rctx):
63 def deserialise(self, dict):
66 def serialise(self, dict):
71 class LCModuleNode(LCNode):
72 def __init__(self, fo, astn: ast.Module):
75 super().__init__(fo, astn)
78 return f"file: {self._fo.filename()}"
80 def prune_astn(self, astn: ast.Module):
84 if not isinstance(b, ast.FunctionDef):
85 general_exprs.append(b)
88 node = LCFuncNode.construct(self._fo, b)
96 with self._env.eval_context(self) as evalc:
97 ls = list(self.__nodes.values())
101 def add_child(self, node):
102 self.__nodes[node] = node
104 def remove_child(self, node):
105 if node in self.__nodes:
106 del self.__nodes[node]
108 def deserialise(self, dict):
109 for node in self.__nodes:
110 node.deserialise(dict)
112 def serialise(self, dict):
113 for node in self.__nodes:
116 def render(self, rctx):
117 for node in self.__nodes:
120 class LCFuncNode(LCNode):
121 def __init__(self, fo, astn) -> None:
125 self._display_name = None
128 super().__init__(fo, astn)
130 def prune_astn(self, astn: ast.FunctionDef):
131 self._name = astn.name
132 self._display_name = to_displayable(self._name)
134 maybe_doc = astn.body[0]
135 if isinstance(maybe_doc, ast.Expr):
136 if isinstance(maybe_doc.value, ast.Constant):
137 self._help = maybe_doc.value.value
138 self._help = textwrap.dedent(self._help)
140 decors = extract_decorators(astn)
141 for name, args, kwargs in decors:
142 self._decors[name] = (args, kwargs)
144 (args, _) = self._decors[self.mapped_name()]
146 self._display_name = args[0]
148 astn.decorator_list.clear()
154 def get_display_name(self):
155 return self._display_name
158 if isinstance(self._parent, LCFuncNode):
159 return self._enabled and self._parent.enabled()
162 def help_prompt(self):
166 with self._env.eval_context(self) as evalc:
167 result = evalc.evaluate()
168 self._enabled = True if result is None else result
171 def mapped_name(self):
175 def construct(fo, astn: ast.FunctionDef):
183 if extract_decorators(astn, node.mapped_name(), True):
184 return node(fo, astn)
186 raise ConfigLoadException(
187 f"unknown type for astn type: {type(astn)}")
189 def set_parent(self, new_parent):
191 self._parent.remove_child(self)
193 new_parent.add_child(self)
194 super().set_parent(new_parent)
197 class LCTermNode(LCFuncNode):
198 def __init__(self, fo, astn) -> None:
205 super().__init__(fo, astn)
211 def prune_astn(self, astn: ast.FunctionDef):
212 astn = super().prune_astn(astn)
214 self._rdonly = "ReadOnly" in self._decors
218 def set_type(self, type_def):
219 self._type = self._env.type_factory().create(type_def)
224 def __assert_type(self, val):
226 raise ConfigLoadException(
227 f"config: {self._name} must be typed", self)
229 if self._type.check(val):
232 raise ConfigTypeCheckError(
233 f"value: {val} does not match type {self._type}", self)
235 def set_value(self, val):
239 if isinstance(val, str):
240 val = self._type.parse_input(val)
242 self.__assert_type(val)
246 self.__update_value()
247 self._env.dependency().cascade(self)
250 def set_default(self, val):
251 self.__assert_type(val)
265 self.__update_value()
270 def render(self, rctx):
272 rctx.add_field(self._display_name, self)
274 def serialise(self, dict):
275 s_val = self._type.serialise(self._value)
276 dict[self._name] = s_val
278 def deserialise(self, dict):
279 if self._name not in dict:
282 s_val = dict[self._name]
283 v = self._type.deserialise(s_val)
284 self.__assert_type(v)
288 def __update_value(self):
291 if not self.enabled():
292 self._env.update_value(self._name, None)
298 if v is None and not self._type.allow_none():
299 raise ConfigLoadException(
300 f"config: {self._name} must have a value", self)
303 self._env.update_value(self._name, v)
308 class LCGroupNode(LCFuncNode):
309 def __init__(self, fo, astn) -> None:
312 super().__init__(fo, astn)
318 def prune_astn(self, astn: ast.FunctionDef):
319 astn = super().prune_astn(astn)
322 for expr in astn.body:
323 if not isinstance(expr, ast.FunctionDef):
324 other_exprs.append(expr)
327 node = LCFuncNode.construct(self._fo, expr)
328 node.set_parent(self)
329 self._children[node] = node
334 lineno=astn.lineno + 1,
335 col_offset=astn.col_offset
339 astn.body = other_exprs
343 old_enable = super().enabled()
346 new_enabled = super().enabled()
347 if not new_enabled and old_enable == new_enabled:
350 with self._env.eval_context(self) as evalc:
351 children = list(self._children.values())
352 for child in children:
355 def render(self, rctx):
356 for child in self._children.values():
359 def serialise(self, dict):
361 for child in self._children.values():
362 child.serialise(sub_dict)
364 dict[self._name] = sub_dict
366 def deserialise(self, dict):
367 if self._name not in dict:
370 sub_dict = dict[self._name]
371 for child in self._children.values():
372 child.deserialise(sub_dict)
374 def add_child(self, node):
375 self._children[node] = node
377 def remove_child(self, node):
378 if node in self._children:
379 del self._children[node]
382 class LCCollectionNode(LCGroupNode):
383 def __init__(self, fo, astn) -> None:
384 super().__init__(fo, astn)
390 def render(self, rctx):