4 from lib.utils import ConfigAST, ConfigASTVisitor
5 from .common import NodeProperty, ConfigNodeError, ValueTypeConstrain
6 from .nodes import GroupNode, TermNode
7 from .sanitiser import TreeSanitiser
9 class NodeBuilder(ConfigASTVisitor):
10 def __init__(self, env):
15 self.__noncfg_astns = []
17 def __pop_and_merge(self):
18 if len(self.__level) == 0:
21 if len(self.__level) == 1:
25 node = self.__level.pop()
26 prev = self.__level[-1]
28 assert isinstance(prev, GroupNode)
32 def __push(self, cfg_node):
33 self.__level.append(cfg_node)
35 def __check_literal_expr(self, node):
37 isinstance(node, ast.Expr)
38 and isinstance(node.value, ast.Constant)
39 and isinstance(node.value.value, str)
42 def _visit_fndef(self, node):
43 if hasattr(node, "__builtin"):
44 self.__noncfg_astns.append(node)
51 cfgn = cfgn_type(self.__env, self.current_file(), node.name)
56 for decor in node.decorator_list:
57 cfgn.apply_decorator(decor)
62 if isinstance(sub, ast.FunctionDef):
63 self._visit_fndef(sub)
66 if self.__check_literal_expr(sub):
67 help_text += sub.value.value
72 NodeProperty.Token[cfgn] = node
73 NodeProperty.HelpText[cfgn] = textwrap.dedent(help_text)
75 if cfgn_type is TermNode:
76 NodeProperty.Type[cfgn] = ValueTypeConstrain(cfgn, node.returns)
78 astns.append(ast.Pass())
79 cfgn.set_node_body(astns)
81 self.__env.register_node(cfgn)
83 except ConfigNodeError as e:
85 except Exception as e:
86 msg = e.args[0] if len(e.args) > 0 else type(e).__name__
87 raise cfgn.config_error(msg)
89 self.__pop_and_merge()
91 def visit(self, node):
94 if isinstance(node, ast.Module):
97 if not isinstance(node, ast.FunctionDef):
98 self.__noncfg_astns.append(node)
101 def build(env, rootfile):
102 build = NodeBuilder(env)
103 ast = ConfigAST(rootfile)
106 ast.visit(TreeSanitiser())
109 env.set_exec_context(build.__noncfg_astns)
110 env.relocate_children()