def extract(fileobj, keywords, comment_tags, options, cls=HTMLTemplate): """ Babel extraction function """ with patch_load(): source = fileobj.read() if isinstance(source, bytes): source = source.decode('UTF-8') template = cls(source) for _, node, _ in im.walk_tree(template.intermediate): for astnode in get_ast_nodes(node): calls = (n for n, _ in astwalk(astnode) if isinstance(n, ast.Call)) for c in calls: if isinstance(c.func, ast.Name) and c.func.id in keywords: strargs = tuple(x.s for x in takewhile( lambda x: isinstance(x, ast.Str), iter(c.args))) if len(strargs) == 1: strargs = strargs[0] if strargs: yield node.pos.line, c.func.id, strargs, [] if isinstance(node, im.TranslationNode): comments = [node.comment] if node.comment else [] func_name = '_' yield (node.pos.line, func_name, node.get_msgstr(), comments)
def _get_out_of_scope_names(fn): """ Return the set of names that are not accessible within node ``fn``. This is good enough for the ast generated by piglet, but would not work more generally as it does not support global and non_local """ in_scope = set() out_of_scope = set() for n, ancestors in astwalk(fn): # A function parameter or py2 variable assignment if isinstance(n, Name) and isinstance(n.ctx, (Param, Store)): in_scope.add((ancestors, n.id)) # A py3 function argument elif arg is not None and isinstance(n, arg): in_scope.add((ancestors, n.arg)) # A name defined through an import elif isinstance(n, alias): in_scope.add((ancestors, n.asname or n.name)) # A reference to a (possibly non-local) variable elif isinstance(n, Name) and isinstance(n.ctx, Load): if not any((ancestors[:ix], n.id) in in_scope for ix in range(len(ancestors) + 1)): out_of_scope.add((n, ancestors)) return out_of_scope
def collect_output(self, parent): """ Context manager that collects any yield expressions added to ``parent`` and turns them into calls to ``__piglet_acc_list.append``. The name of the accumulator object is returned by the function """ acc = self.unique_id('acc') append = self.unique_id('append') pos = len(parent.body) parent.body.append(Assign(targets=[StoreName(acc)], value=List(elts=[], ctx=Load()))) parent.body.append(Assign(targets=[StoreName(append)], value=Attribute(value=LoadName(acc), attr='append', ctx=Load()))) yield acc for n in parent.body[pos:]: for node, ancestors in astwalk(n): if isinstance(node, Expr) and isinstance(node.value, Yield): node.value = Call(func=LoadName(append), args=[node.value.value], starargs=None, kwargs=None, keywords=[])
def _ensure_all_functions_yield(module): """ All generated functions should contain at least one yield statement. This walks the ast to insert a "yield ''" in functions that don't otherwise produce output (eg in the case of '<py:def function="a"></py:def>') """ functions = {} if YieldFrom is not None: yield_classes = (Yield, YieldFrom) else: yield_classes = (Yield,) for node, ancestors in astwalk(module): if isinstance(node, FunctionDef): functions.setdefault(node, False) elif isinstance(node, yield_classes): f = next(a for a in reversed(ancestors) if isinstance(a, FunctionDef)) functions[f] = True for f in functions: if not functions[f]: f.body.append(Expr(Yield(Str(s='')))) return module
def get_ancestors(self, node): """ Return the sequence of ancestor nodes for ``node``. :param astnode: the astnode to consider :yields: sequence of ancestor AST nodes, in the order ``[node, parent, grandparent, great-grandparent, ...]`` """ for n, ancestors in astwalk(self.module): if n is node: return chain([node], reversed(ancestors)) return []
def _get_function_defs(astnode): return ((n, a) for n, a in astwalk(astnode) if isinstance(n, FunctionDef))