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:
204 super().__init__(fo, astn)
210 def prune_astn(self, astn: ast.FunctionDef):
211 astn = super().prune_astn(astn)
213 self._rdonly = "ReadOnly" in self._decors
217 def set_type(self, type_def):
218 self._type = self._env.type_factory().create(type_def)
223 def __assert_type(self, val):
225 raise ConfigLoadException(
226 f"config: {self._name} must be typed", self)
228 if self._type.check(val):
231 raise ConfigTypeCheckError(
232 f"value: {val} does not match type {self._type}", self)
234 def set_value(self, val):
238 if isinstance(val, str):
239 val = self._type.parse_input(val)
241 self.__assert_type(val)
243 self.__update_value()
244 self._env.dependency().cascade(self)
246 def set_default(self, val):
247 self.__assert_type(val)
258 self.__update_value()
263 def render(self, rctx):
265 rctx.add_field(self._display_name, self)
267 def serialise(self, dict):
268 s_val = self._type.serialise(self._value)
269 dict[self._name] = s_val
271 def deserialise(self, dict):
272 if self._name not in dict:
275 s_val = dict[self._name]
276 v = self._type.deserialise(s_val)
277 self.__assert_type(v)
281 def __update_value(self):
284 if not self.enabled():
285 self._env.update_value(self._name, None)
291 if v is None and not self._type.allow_none():
292 raise ConfigLoadException(
293 f"config: {self._name} must have a value", self)
296 self._env.update_value(self._name, v)
301 class LCGroupNode(LCFuncNode):
302 def __init__(self, fo, astn) -> None:
305 super().__init__(fo, astn)
311 def prune_astn(self, astn: ast.FunctionDef):
312 astn = super().prune_astn(astn)
315 for expr in astn.body:
316 if not isinstance(expr, ast.FunctionDef):
317 other_exprs.append(expr)
320 node = LCFuncNode.construct(self._fo, expr)
321 node.set_parent(self)
322 self._children[node] = node
327 lineno=astn.lineno + 1,
328 col_offset=astn.col_offset
332 astn.body = other_exprs
336 old_enable = super().enabled()
339 new_enabled = super().enabled()
340 if not new_enabled and old_enable == new_enabled:
343 with self._env.eval_context(self) as evalc:
344 children = list(self._children.values())
345 for child in children:
348 def render(self, rctx):
349 for child in self._children.values():
352 def serialise(self, dict):
354 for child in self._children.values():
355 child.serialise(sub_dict)
357 dict[self._name] = sub_dict
359 def deserialise(self, dict):
360 if self._name not in dict:
363 sub_dict = dict[self._name]
364 for child in self._children.values():
365 child.deserialise(sub_dict)
367 def add_child(self, node):
368 self._children[node] = node
370 def remove_child(self, node):
371 if node in self._children:
372 del self._children[node]
375 class LCCollectionNode(LCGroupNode):
376 def __init__(self, fo, astn) -> None:
377 super().__init__(fo, astn)
383 def render(self, rctx):