5 from lib.utils import ConfigAST, ConfigASTVisitor
6 from .common import NodeProperty, ConfigNodeError, ValueTypeConstrain
7 from .nodes import GroupNode, TermNode
8 from .sanitiser import TreeSanitiser
10 class NodeBuilder(ConfigASTVisitor):
11 def __init__(self, env):
16 self.__noncfg_astns = []
18 def __pop_and_merge(self):
19 if len(self.__level) == 0:
22 if len(self.__level) == 1:
26 node = self.__level.pop()
27 prev = self.__level[-1]
29 assert isinstance(prev, GroupNode)
33 def __push(self, cfg_node):
34 self.__level.append(cfg_node)
36 def __check_literal_expr(self, node):
38 isinstance(node, ast.Expr)
39 and isinstance(node.value, ast.Constant)
40 and isinstance(node.value.value, str)
43 def _visit_fndef(self, node):
44 if hasattr(node, "__builtin"):
45 self.__noncfg_astns.append(node)
52 cfgn = cfgn_type(self.__env, self.current_file(), node.name)
57 for decor in node.decorator_list:
58 cfgn.apply_decorator(decor)
63 if isinstance(sub, ast.FunctionDef):
64 self._visit_fndef(sub)
67 if self.__check_literal_expr(sub):
68 help_text += sub.value.value
73 NodeProperty.Token[cfgn] = node
74 NodeProperty.HelpText[cfgn] = textwrap.dedent(help_text)
76 if cfgn_type is TermNode:
77 NodeProperty.Type[cfgn] = ValueTypeConstrain(cfgn, node.returns)
79 astns.append(ast.Pass())
80 cfgn.set_node_body(astns)
82 self.__env.register_node(cfgn)
84 except ConfigNodeError as e:
86 except Exception as e:
87 msg = e.args[0] if len(e.args) > 0 else type(e).__name__
88 raise cfgn.config_error(msg)
90 self.__pop_and_merge()
92 def visit(self, node):
95 if isinstance(node, ast.Module):
98 if not isinstance(node, ast.FunctionDef):
99 self.__noncfg_astns.append(node)
102 def build(env, rootfile):
103 if not os.path.exists(rootfile):
104 print(f"warning: config file '{rootfile}' not detected, skipped")
107 build = NodeBuilder(env)
108 ast = ConfigAST(rootfile)
111 ast.visit(TreeSanitiser())
114 for node in env.nodes():
115 node.apply_node_body()
117 env.set_exec_context(build.__noncfg_astns)
118 env.relocate_children()