mirror of https://github.com/chriskiehl/Gooey.git
4 changed files with 0 additions and 865 deletions
Split View
Diff Options
-
61gooey/python_bindings/code_prep.py
-
578gooey/python_bindings/codegen.py
-
171gooey/python_bindings/source_parser.py
-
55gooey/tests/code_prep_unittest.py
@ -1,61 +0,0 @@ |
|||
__author__ = 'Chris' |
|||
|
|||
""" |
|||
Preps the extracted Python code so that it can be evaled by the |
|||
monkey_parser |
|||
""" |
|||
|
|||
from itertools import * |
|||
|
|||
source = ''' |
|||
import sys |
|||
import os |
|||
import doctest |
|||
import cProfile |
|||
import pstats |
|||
from argparse import ArgumentParser |
|||
from argparse import RawDescriptionHelpFormatter |
|||
from gooey import Gooey |
|||
parser = ArgumentParser(description='Example Argparse Program', formatter_class=RawDescriptionHelpFormatter) |
|||
parser.add_argument('filename', help='filename') |
|||
parser.add_argument('-r', '--recursive', dest='recurse', action='store_true', help='recurse into subfolders [default: %(default)s]') |
|||
parser.add_argument('-v', '--verbose', dest='verbose', action='count', help='set verbosity level [default: %(default)s]') |
|||
parser.add_argument('-i', '--include', action='append', help='only include paths matching this regex pattern. Note: exclude is given preference over include. [default: %(default)s]', metavar='RE') |
|||
parser.add_argument('-m', '--mycoolargument', help='mycoolargument') |
|||
parser.add_argument('-e', '--exclude', dest='exclude', help='exclude paths matching this regex pattern. [default: %(default)s]', metavar='RE') |
|||
parser.add_argument('-V', '--version', action='version') |
|||
parser.add_argument('-T', '--tester', choices=['yes', 'no']) |
|||
parser.add_argument(dest='paths', help='paths to folder(s) with source file(s) [default: %(default)s]', metavar='path', nargs='+') |
|||
''' |
|||
|
|||
def take_imports(code): |
|||
return takewhile(lambda line: 'import' in line, code) |
|||
|
|||
def drop_imports(code): |
|||
return dropwhile(lambda line: 'import' in line, code) |
|||
|
|||
def split_line(line): |
|||
# splits an assignment statement into varname and command strings |
|||
# in: "parser = ArgumentParser(description='Example Argparse Program')" |
|||
# out: "parser", "ArgumentParser(description='Example Argparse Program" |
|||
variable, instruction = line.split('=', 1) |
|||
return variable.strip(), instruction.strip() |
|||
|
|||
def update_parser_varname(new_varname, code): |
|||
# lines = source.split('\n')[1:] |
|||
lines = filter(lambda x: x != '', code) |
|||
|
|||
argparse_code = dropwhile(lambda line: 'import' in line, lines) |
|||
old_argparser_varname, _ = split_line(argparse_code.next()) |
|||
|
|||
updated_code = [line.replace(old_argparser_varname, new_varname) |
|||
for line in lines] |
|||
return updated_code |
|||
|
|||
if __name__ == '__main__': |
|||
pass |
|||
|
|||
|
|||
|
|||
|
|||
|
@ -1,578 +0,0 @@ |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
codegen |
|||
~~~~~~~ |
|||
|
|||
Extension to ast that allow ast -> python code generation. |
|||
|
|||
:copyright: Copyright 2008 by Armin Ronacher. |
|||
:license: BSD. |
|||
""" |
|||
from ast import * |
|||
|
|||
|
|||
BOOLOP_SYMBOLS = { |
|||
And: 'and', |
|||
Or: 'or' |
|||
} |
|||
|
|||
BINOP_SYMBOLS = { |
|||
Add: '+', |
|||
Sub: '-', |
|||
Mult: '*', |
|||
Div: '/', |
|||
FloorDiv: '//', |
|||
Mod: '%', |
|||
LShift: '<<', |
|||
RShift: '>>', |
|||
BitOr: '|', |
|||
BitAnd: '&', |
|||
BitXor: '^' |
|||
} |
|||
|
|||
CMPOP_SYMBOLS = { |
|||
Eq: '==', |
|||
Gt: '>', |
|||
GtE: '>=', |
|||
In: 'in', |
|||
Is: 'is', |
|||
IsNot: 'is not', |
|||
Lt: '<', |
|||
LtE: '<=', |
|||
NotEq: '!=', |
|||
NotIn: 'not in' |
|||
} |
|||
|
|||
UNARYOP_SYMBOLS = { |
|||
Invert: '~', |
|||
Not: 'not', |
|||
UAdd: '+', |
|||
USub: '-' |
|||
} |
|||
|
|||
ALL_SYMBOLS = {} |
|||
ALL_SYMBOLS.update(BOOLOP_SYMBOLS) |
|||
ALL_SYMBOLS.update(BINOP_SYMBOLS) |
|||
ALL_SYMBOLS.update(CMPOP_SYMBOLS) |
|||
ALL_SYMBOLS.update(UNARYOP_SYMBOLS) |
|||
|
|||
|
|||
def to_source(node, indent_with=' ' * 4, add_line_information=False): |
|||
"""This function can convert a node tree back into python sourcecode. |
|||
This is useful for debugging purposes, especially if you're dealing with |
|||
custom asts not generated by python itself. |
|||
|
|||
It could be that the sourcecode is evaluable when the AST itself is not |
|||
compilable / evaluable. The reason for this is that the AST contains some |
|||
more data than regular sourcecode does, which is dropped during |
|||
conversion. |
|||
|
|||
Each level of indentation is replaced with `indent_with`. Per default this |
|||
parameter is equal to four spaces as suggested by PEP 8, but it might be |
|||
adjusted to match the application's styleguide. |
|||
|
|||
If `add_line_information` is set to `True` comments for the line numbers |
|||
of the nodes are added to the output. This can be used to spot wrong line |
|||
number information of statement nodes. |
|||
""" |
|||
generator = SourceGenerator(indent_with, add_line_information) |
|||
generator.visit(node) |
|||
return ''.join(str(s) for s in generator.result) |
|||
|
|||
|
|||
class SourceGenerator(NodeVisitor): |
|||
"""This visitor is able to transform a well formed syntax tree into python |
|||
sourcecode. For more details have a look at the docstring of the |
|||
`node_to_source` function. |
|||
""" |
|||
|
|||
def __init__(self, indent_with, add_line_information=False): |
|||
self.result = [] |
|||
self.indent_with = indent_with |
|||
self.add_line_information = add_line_information |
|||
self.indentation = 0 |
|||
self.new_lines = 0 |
|||
|
|||
def write(self, x): |
|||
if self.new_lines: |
|||
if self.result: |
|||
self.result.append('\n' * self.new_lines) |
|||
self.result.append(self.indent_with * self.indentation) |
|||
self.new_lines = 0 |
|||
self.result.append(x) |
|||
|
|||
def newline(self, node=None, extra=0): |
|||
self.new_lines = max(self.new_lines, 1 + extra) |
|||
if node is not None and self.add_line_information: |
|||
self.write('# line: %s' % node.lineno) |
|||
self.new_lines = 1 |
|||
|
|||
def body(self, statements): |
|||
self.new_line = True |
|||
self.indentation += 1 |
|||
for stmt in statements: |
|||
self.visit(stmt) |
|||
self.indentation -= 1 |
|||
|
|||
def body_or_else(self, node): |
|||
self.body(node.body) |
|||
if node.orelse: |
|||
self.newline() |
|||
self.write('else:') |
|||
self.body(node.orelse) |
|||
|
|||
def signature(self, node): |
|||
want_comma = [] |
|||
|
|||
def write_comma(): |
|||
if want_comma: |
|||
self.write(', ') |
|||
else: |
|||
want_comma.append(True) |
|||
|
|||
padding = [None] * (len(node.args) - len(node.defaults)) |
|||
for arg, default in zip(node.args, padding + node.defaults): |
|||
write_comma() |
|||
self.visit(arg) |
|||
if default is not None: |
|||
self.write('=') |
|||
self.visit(default) |
|||
if node.vararg is not None: |
|||
write_comma() |
|||
self.write('*' + node.vararg) |
|||
if node.kwarg is not None: |
|||
write_comma() |
|||
self.write('**' + node.kwarg) |
|||
|
|||
def decorators(self, node): |
|||
for decorator in node.decorator_list: |
|||
self.newline(decorator) |
|||
self.write('@') |
|||
self.visit(decorator) |
|||
|
|||
# Statements |
|||
|
|||
def visit_Assign(self, node): |
|||
self.newline(node) |
|||
for idx, target in enumerate(node.targets): |
|||
if idx: |
|||
self.write(', ') |
|||
self.visit(target) |
|||
self.write(' = ') |
|||
self.visit(node.value) |
|||
|
|||
def visit_AugAssign(self, node): |
|||
self.newline(node) |
|||
self.visit(node.target) |
|||
self.write(BINOP_SYMBOLS[type(node.op)] + '=') |
|||
self.visit(node.value) |
|||
|
|||
def visit_ImportFrom(self, node): |
|||
self.newline(node) |
|||
self.write('from %s%s import ' % ('.' * node.level, node.module)) |
|||
for idx, item in enumerate(node.names): |
|||
if idx: |
|||
self.write(', ') |
|||
self.write(item.name) |
|||
|
|||
def visit_Import(self, node): |
|||
self.newline(node) |
|||
for item in node.names: |
|||
self.write('import ') |
|||
self.visit(item) |
|||
|
|||
def visit_Expr(self, node): |
|||
self.newline(node) |
|||
self.generic_visit(node) |
|||
|
|||
def visit_FunctionDef(self, node): |
|||
self.newline(extra=1) |
|||
self.decorators(node) |
|||
self.newline(node) |
|||
self.write('def %s(' % node.name) |
|||
self.signature(node.args) |
|||
self.write('):') |
|||
self.body(node.body) |
|||
|
|||
def visit_ClassDef(self, node): |
|||
have_args = [] |
|||
|
|||
def paren_or_comma(): |
|||
if have_args: |
|||
self.write(', ') |
|||
else: |
|||
have_args.append(True) |
|||
self.write('(') |
|||
|
|||
self.newline(extra=2) |
|||
self.decorators(node) |
|||
self.newline(node) |
|||
self.write('class %s' % node.name) |
|||
for base in node.bases: |
|||
paren_or_comma() |
|||
self.visit(base) |
|||
# XXX: the if here is used to keep this module compatible |
|||
# with python 2.6. |
|||
if hasattr(node, 'keywords'): |
|||
for keyword in node.keywords: |
|||
paren_or_comma() |
|||
self.write(keyword.arg + '=') |
|||
self.visit(keyword.value) |
|||
if node.starargs is not None: |
|||
paren_or_comma() |
|||
self.write('*') |
|||
self.visit(node.starargs) |
|||
if node.kwargs is not None: |
|||
paren_or_comma() |
|||
self.write('**') |
|||
self.visit(node.kwargs) |
|||
self.write(have_args and '):' or ':') |
|||
self.body(node.body) |
|||
|
|||
def visit_If(self, node): |
|||
self.newline(node) |
|||
self.write('if ') |
|||
self.visit(node.test) |
|||
self.write(':') |
|||
self.body(node.body) |
|||
while True: |
|||
else_ = node.orelse |
|||
if len(else_) == 1 and isinstance(else_[0], If): |
|||
node = else_[0] |
|||
self.newline() |
|||
self.write('elif ') |
|||
self.visit(node.test) |
|||
self.write(':') |
|||
self.body(node.body) |
|||
else: |
|||
self.newline() |
|||
self.write('else:') |
|||
self.body(else_) |
|||
break |
|||
|
|||
def visit_For(self, node): |
|||
self.newline(node) |
|||
self.write('for ') |
|||
self.visit(node.target) |
|||
self.write(' in ') |
|||
self.visit(node.iter) |
|||
self.write(':') |
|||
self.body_or_else(node) |
|||
|
|||
def visit_While(self, node): |
|||
self.newline(node) |
|||
self.write('while ') |
|||
self.visit(node.test) |
|||
self.write(':') |
|||
self.body_or_else(node) |
|||
|
|||
def visit_With(self, node): |
|||
self.newline(node) |
|||
self.write('with ') |
|||
self.visit(node.context_expr) |
|||
if node.optional_vars is not None: |
|||
self.write(' as ') |
|||
self.visit(node.optional_vars) |
|||
self.write(':') |
|||
self.body(node.body) |
|||
|
|||
def visit_Pass(self, node): |
|||
self.newline(node) |
|||
self.write('pass') |
|||
|
|||
def visit_Print(self, node): |
|||
# XXX: python 2.6 only |
|||
self.newline(node) |
|||
self.write('print ') |
|||
want_comma = False |
|||
if node.dest is not None: |
|||
self.write(' >> ') |
|||
self.visit(node.dest) |
|||
want_comma = True |
|||
for value in node.values: |
|||
if want_comma: |
|||
self.write(', ') |
|||
self.visit(value) |
|||
want_comma = True |
|||
if not node.nl: |
|||
self.write(',') |
|||
|
|||
def visit_Delete(self, node): |
|||
self.newline(node) |
|||
self.write('del ') |
|||
for idx, target in enumerate(node): |
|||
if idx: |
|||
self.write(', ') |
|||
self.visit(target) |
|||
|
|||
def visit_TryExcept(self, node): |
|||
self.newline(node) |
|||
self.write('try:') |
|||
self.body(node.body) |
|||
for handler in node.handlers: |
|||
self.visit(handler) |
|||
|
|||
def visit_TryFinally(self, node): |
|||
self.newline(node) |
|||
self.write('try:') |
|||
self.body(node.body) |
|||
self.newline(node) |
|||
self.write('finally:') |
|||
self.body(node.finalbody) |
|||
|
|||
def visit_Global(self, node): |
|||
self.newline(node) |
|||
self.write('global ' + ', '.join(node.names)) |
|||
|
|||
def visit_Nonlocal(self, node): |
|||
self.newline(node) |
|||
self.write('nonlocal ' + ', '.join(node.names)) |
|||
|
|||
def visit_Return(self, node): |
|||
self.newline(node) |
|||
if node.value: |
|||
self.write('return ') |
|||
self.visit(node.value) |
|||
else: |
|||
self.write('return') |
|||
|
|||
def visit_Break(self, node): |
|||
self.newline(node) |
|||
self.write('break') |
|||
|
|||
def visit_Continue(self, node): |
|||
self.newline(node) |
|||
self.write('continue') |
|||
|
|||
def visit_Raise(self, node): |
|||
# XXX: Python 2.6 / 3.0 compatibility |
|||
self.newline(node) |
|||
self.write('raise') |
|||
if hasattr(node, 'exc') and node.exc is not None: |
|||
self.write(' ') |
|||
self.visit(node.exc) |
|||
if node.cause is not None: |
|||
self.write(' from ') |
|||
self.visit(node.cause) |
|||
elif hasattr(node, 'type') and node.type is not None: |
|||
self.visit(node.type) |
|||
if node.inst is not None: |
|||
self.write(', ') |
|||
self.visit(node.inst) |
|||
if node.tback is not None: |
|||
self.write(', ') |
|||
self.visit(node.tback) |
|||
|
|||
# Expressions |
|||
|
|||
def visit_Attribute(self, node): |
|||
self.visit(node.value) |
|||
self.write('.' + node.attr) |
|||
|
|||
def visit_Call(self, node): |
|||
want_comma = [] |
|||
|
|||
def write_comma(): |
|||
if want_comma: |
|||
self.write(', ') |
|||
else: |
|||
want_comma.append(True) |
|||
|
|||
self.visit(node.func) |
|||
self.write('(') |
|||
for arg in node.args: |
|||
write_comma() |
|||
self.visit(arg) |
|||
for keyword in node.keywords: |
|||
write_comma() |
|||
self.write(keyword.arg + '=') |
|||
self.visit(keyword.value) |
|||
if node.starargs is not None: |
|||
write_comma() |
|||
self.write('*') |
|||
self.visit(node.starargs) |
|||
if node.kwargs is not None: |
|||
write_comma() |
|||
self.write('**') |
|||
self.visit(node.kwargs) |
|||
self.write(')') |
|||
|
|||
def visit_Name(self, node): |
|||
self.write(node.id) |
|||
|
|||
def visit_Str(self, node): |
|||
self.write(repr(node.s)) |
|||
|
|||
def visit_Bytes(self, node): |
|||
self.write(repr(node.s)) |
|||
|
|||
def visit_Num(self, node): |
|||
self.write(repr(node.n)) |
|||
|
|||
def visit_Tuple(self, node): |
|||
self.write('(') |
|||
idx = -1 |
|||
for idx, item in enumerate(node.elts): |
|||
if idx: |
|||
self.write(', ') |
|||
self.visit(item) |
|||
self.write(idx and ')' or ',)') |
|||
|
|||
def sequence_visit(left, right): |
|||
def visit(self, node): |
|||
self.write(left) |
|||
for idx, item in enumerate(node.elts): |
|||
if idx: |
|||
self.write(', ') |
|||
self.visit(item) |
|||
self.write(right) |
|||
return visit |
|||
|
|||
visit_List = sequence_visit('[', ']') |
|||
visit_Set = sequence_visit('{', '}') |
|||
del sequence_visit |
|||
|
|||
def visit_Dict(self, node): |
|||
self.write('{') |
|||
for idx, (key, value) in enumerate(zip(node.keys, node.values)): |
|||
if idx: |
|||
self.write(', ') |
|||
self.visit(key) |
|||
self.write(': ') |
|||
self.visit(value) |
|||
self.write('}') |
|||
|
|||
def visit_BinOp(self, node): |
|||
self.visit(node.left) |
|||
self.write(' %s ' % BINOP_SYMBOLS[type(node.op)]) |
|||
self.visit(node.right) |
|||
|
|||
def visit_BoolOp(self, node): |
|||
self.write('(') |
|||
for idx, value in enumerate(node.values): |
|||
if idx: |
|||
self.write(' %s ' % BOOLOP_SYMBOLS[type(node.op)]) |
|||
self.visit(value) |
|||
self.write(')') |
|||
|
|||
def visit_Compare(self, node): |
|||
self.write('(') |
|||
self.write(node.left) |
|||
for op, right in zip(node.ops, node.comparators): |
|||
self.write(' %s %%' % CMPOP_SYMBOLS[type(op)]) |
|||
self.visit(right) |
|||
self.write(')') |
|||
|
|||
def visit_UnaryOp(self, node): |
|||
self.write('(') |
|||
op = UNARYOP_SYMBOLS[type(node.op)] |
|||
self.write(op) |
|||
if op == 'not': |
|||
self.write(' ') |
|||
self.visit(node.operand) |
|||
self.write(')') |
|||
|
|||
def visit_Subscript(self, node): |
|||
self.visit(node.value) |
|||
self.write('[') |
|||
self.visit(node.slice) |
|||
self.write(']') |
|||
|
|||
def visit_Slice(self, node): |
|||
if node.lower is not None: |
|||
self.visit(node.lower) |
|||
self.write(':') |
|||
if node.upper is not None: |
|||
self.visit(node.upper) |
|||
if node.step is not None: |
|||
self.write(':') |
|||
if not (isinstance(node.step, Name) and node.step.id == 'None'): |
|||
self.visit(node.step) |
|||
|
|||
def visit_ExtSlice(self, node): |
|||
for idx, item in node.dims: |
|||
if idx: |
|||
self.write(', ') |
|||
self.visit(item) |
|||
|
|||
def visit_Yield(self, node): |
|||
self.write('yield ') |
|||
self.visit(node.value) |
|||
|
|||
def visit_Lambda(self, node): |
|||
self.write('lambda ') |
|||
self.signature(node.args) |
|||
self.write(': ') |
|||
self.visit(node.body) |
|||
|
|||
def visit_Ellipsis(self, node): |
|||
self.write('Ellipsis') |
|||
|
|||
def generator_visit(left, right): |
|||
def visit(self, node): |
|||
self.write(left) |
|||
self.visit(node.elt) |
|||
for comprehension in node.generators: |
|||
self.visit(comprehension) |
|||
self.write(right) |
|||
return visit |
|||
|
|||
visit_ListComp = generator_visit('[', ']') |
|||
visit_GeneratorExp = generator_visit('(', ')') |
|||
visit_SetComp = generator_visit('{', '}') |
|||
del generator_visit |
|||
|
|||
def visit_DictComp(self, node): |
|||
self.write('{') |
|||
self.visit(node.key) |
|||
self.write(': ') |
|||
self.visit(node.value) |
|||
for comprehension in node.generators: |
|||
self.visit(comprehension) |
|||
self.write('}') |
|||
|
|||
def visit_IfExp(self, node): |
|||
self.visit(node.body) |
|||
self.write(' if ') |
|||
self.visit(node.test) |
|||
self.write(' else ') |
|||
self.visit(node.orelse) |
|||
|
|||
def visit_Starred(self, node): |
|||
self.write('*') |
|||
self.visit(node.value) |
|||
|
|||
def visit_Repr(self, node): |
|||
# XXX: python 2.6 only |
|||
self.write('`') |
|||
self.visit(node.value) |
|||
self.write('`') |
|||
|
|||
# Helper Nodes |
|||
|
|||
def visit_alias(self, node): |
|||
self.write(node.name) |
|||
if node.asname is not None: |
|||
self.write(' as ' + node.asname) |
|||
|
|||
def visit_comprehension(self, node): |
|||
self.write(' for ') |
|||
self.visit(node.target) |
|||
self.write(' in ') |
|||
self.visit(node.iter) |
|||
if node.ifs: |
|||
for if_ in node.ifs: |
|||
self.write(' if ') |
|||
self.visit(if_) |
|||
|
|||
def visit_excepthandler(self, node): |
|||
self.newline(node) |
|||
self.write('except') |
|||
if node.type is not None: |
|||
self.write(' ') |
|||
self.visit(node.type) |
|||
if node.name is not None: |
|||
self.write(' as ') |
|||
self.visit(node.name) |
|||
self.write(':') |
|||
self.body(node.body) |
@ -1,171 +0,0 @@ |
|||
''' |
|||
Created on Dec 11, 2013 |
|||
|
|||
@author: Chris |
|||
|
|||
Collection of functions for extracting argparse related statements from the |
|||
client code. |
|||
''' |
|||
|
|||
import re |
|||
import os |
|||
import ast |
|||
import _ast |
|||
from itertools import * |
|||
|
|||
from gooey.python_bindings import codegen, modules |
|||
|
|||
|
|||
def parse_source_file(file_name): |
|||
""" |
|||
Parses the AST of Python file for lines containing |
|||
references to the argparse module. |
|||
|
|||
returns the collection of ast objects found. |
|||
|
|||
Example client code: |
|||
|
|||
1. parser = ArgumentParser(desc="My help Message") |
|||
2. parser.add_argument('filename', help="Name of the file to load") |
|||
3. parser.add_argument('-f', '--format', help='Format of output \nOptions: ['md', 'html'] |
|||
4. args = parser.parse_args() |
|||
|
|||
Variables: |
|||
* nodes Primary syntax tree object |
|||
* argparse_assignments The assignment of the ArgumentParser (line 1 in example code) |
|||
* add_arg_assignments Calls to add_argument() (lines 2-3 in example code) |
|||
* parser_var_name The instance variable of the ArgumentParser (line 1 in example code) |
|||
* ast_source The curated collection of all parser related nodes in the client code |
|||
""" |
|||
|
|||
nodes = ast.parse(_openfile(file_name)) |
|||
|
|||
module_imports = get_nodes_by_instance_type(nodes, _ast.Import) |
|||
specific_imports = get_nodes_by_instance_type(nodes, _ast.ImportFrom) |
|||
|
|||
assignment_objs = get_nodes_by_instance_type(nodes, _ast.Assign) |
|||
call_objects = get_nodes_by_instance_type(nodes, _ast.Call) |
|||
|
|||
argparse_assignments = get_nodes_by_containing_attr(assignment_objs, 'ArgumentParser') |
|||
add_arg_assignments = get_nodes_by_containing_attr(call_objects, 'add_argument') |
|||
parse_args_assignment = get_nodes_by_containing_attr(call_objects, 'parse_args') |
|||
|
|||
ast_argparse_source = chain( |
|||
module_imports, |
|||
specific_imports, |
|||
argparse_assignments, |
|||
add_arg_assignments |
|||
# parse_args_assignment |
|||
) |
|||
return ast_argparse_source |
|||
|
|||
def _openfile(file_name): |
|||
with open(file_name, 'rb') as f: |
|||
return f.read() |
|||
|
|||
def read_client_module(filename): |
|||
with open(filename, 'r') as f: |
|||
return f.readlines() |
|||
|
|||
def get_nodes_by_instance_type(nodes, object_type): |
|||
return [node for node in walk_tree(nodes) if isinstance(node, object_type)] |
|||
|
|||
def get_nodes_by_containing_attr(nodes, attr): |
|||
return [node for node in nodes if attr in walk_tree(node)] |
|||
|
|||
def walk_tree(node): |
|||
yield node |
|||
d = node.__dict__ |
|||
for key, value in d.iteritems(): |
|||
if isinstance(value, list): |
|||
for val in value: |
|||
for _ in walk_tree(val): yield _ |
|||
elif 'ast' in str(type(value)): |
|||
for _ in walk_tree(value): yield _ |
|||
else: |
|||
yield value |
|||
|
|||
|
|||
def convert_to_python(ast_source): |
|||
""" |
|||
Converts the ast objects back into human readable Python code |
|||
""" |
|||
return map(codegen.to_source, ast_source) |
|||
|
|||
def get_assignment_name(lines): |
|||
nodes = ast.parse(''.join(lines)) |
|||
assignments = get_nodes_by_instance_type(nodes, _ast.Assign) |
|||
argparse_var = get_nodes_by_containing_attr(assignments, 'parse_args') |
|||
return argparse_var[0].value.func.value.id |
|||
|
|||
|
|||
def lines_indented(line): |
|||
unindented = re.compile("^[a-zA-Z0-9_@]+") |
|||
return unindented.match(line) is None |
|||
|
|||
def not_at_main(line): |
|||
return 'def main' not in line |
|||
|
|||
def not_at_parse_args(line): |
|||
return 'parse_args(' not in line |
|||
|
|||
def get_indent(line): |
|||
indent = re.compile("(\t|\s)") |
|||
return ''.join(takewhile(lambda char: indent.match(char) is not None, line)) |
|||
|
|||
def format_source_to_return_parser(source, cutoff_line, restart_line, col_offset, parser_name): |
|||
top = source[:cutoff_line - 1] |
|||
bottom = source[restart_line:] |
|||
indentation = source[cutoff_line - 1][:col_offset] |
|||
return_statement = ['{}return {}\n\n'.format(indentation, parser_name)] |
|||
|
|||
# stitch it all back together excluding the Gooey decorator |
|||
new_source = (line for line in chain(top, return_statement, bottom) |
|||
if '@gooey' not in line.lower()) |
|||
|
|||
return ''.join(new_source) |
|||
|
|||
def extract_parser(modulepath, func_with_argparse): |
|||
source = read_client_module(modulepath) |
|||
|
|||
nodes = ast.parse(''.join(source)) |
|||
funcs = get_nodes_by_instance_type(nodes, _ast.FunctionDef) |
|||
assignment_objs = get_nodes_by_instance_type(nodes, _ast.Assign) |
|||
|
|||
main_func = get_nodes_by_containing_attr(funcs, func_with_argparse)[0] |
|||
parse_args_assignment = get_nodes_by_containing_attr(main_func.body, 'parse_args')[0] |
|||
|
|||
# ast reports the line no of a block structure as the start of the structure, |
|||
# not the end, so we look for the line no of the next node after main() |
|||
# and use that as the end of the main() function. |
|||
try: |
|||
restart_line = nodes.body[nodes.body.index(main_func)+1].lineno - 1 |
|||
except IndexError: |
|||
restart_line = len(source) |
|||
|
|||
module_source = format_source_to_return_parser( |
|||
source, |
|||
cutoff_line=parse_args_assignment.lineno, |
|||
restart_line=restart_line, |
|||
col_offset=parse_args_assignment.col_offset, |
|||
parser_name=parse_args_assignment.value.func.value.id |
|||
) |
|||
client_module = modules.load(module_source) |
|||
return getattr(client_module, func_with_argparse)() |
|||
|
|||
|
|||
def has_argparse(source): |
|||
return any(['.parse_args(' in line.lower() for line in source.split('\n')]) |
|||
|
|||
if __name__ == '__main__': |
|||
filepath = os.path.join(os.path.dirname(__file__), |
|||
'examples', |
|||
'example_argparse_souce_in_main.py') |
|||
|
|||
nodes = ast.parse(_openfile(filepath)) |
|||
# |
|||
ast_source = parse_source_file(filepath) |
|||
python_code = convert_to_python(list(ast_source)) |
|||
|
|||
|
|||
|
@ -1,55 +0,0 @@ |
|||
__author__ = 'Chris' |
|||
|
|||
import unittest |
|||
from gooey.python_bindings import code_prep |
|||
|
|||
|
|||
def test_split_line(): |
|||
line = "parser = ArgumentParser(description='Example Argparse Program')" |
|||
assert "parser" == code_prep.split_line(line)[0] |
|||
assert "ArgumentParser(description='Example Argparse Program')" == code_prep.split_line(line)[1] |
|||
|
|||
|
|||
def test_update_parser_varname_assigns_new_name_to_parser_var(): |
|||
line = ["parser = ArgumentParser(description='Example Argparse Program')"] |
|||
expected = "jarser = ArgumentParser(description='Example Argparse Program')" |
|||
result = code_prep.update_parser_varname('jarser', line)[0] |
|||
assert result == expected |
|||
|
|||
def test_update_parser_varname_assigns_new_name_to_parser_var__multiline(): |
|||
lines = ''' |
|||
import argparse |
|||
from argparse import ArgumentParser |
|||
parser = ArgumentParser(description='Example Argparse Program') |
|||
parser.parse_args() |
|||
'''.split('\n') |
|||
|
|||
line = "jarser = ArgumentParser(description='Example Argparse Program')" |
|||
result = code_prep.update_parser_varname('jarser', lines)[2] |
|||
assert line == result |
|||
|
|||
|
|||
def test_take_imports_drops_all_non_imports_statements(): |
|||
lines = ''' |
|||
import argparse |
|||
from argparse import ArgumentParser |
|||
parser = ArgumentParser(description='Example Argparse Program') |
|||
parser.parse_args() |
|||
'''.split('\n')[1:] |
|||
|
|||
assert 2 == len(list(code_prep.take_imports(lines))) |
|||
assert 'import argparse' == list(code_prep.take_imports(lines))[0] |
|||
|
|||
|
|||
def test_drop_imports_excludes_all_imports_statements(): |
|||
lines = ''' |
|||
import argparse |
|||
from argparse import ArgumentParser |
|||
parser = ArgumentParser(description='Example Argparse Program') |
|||
parser.parse_args() |
|||
'''.split('\n')[1:] |
|||
|
|||
assert 2 == len(list(code_prep.take_imports(lines))) |
|||
assert 'parser.parse_args()' == list(code_prep.drop_imports(lines))[1] |
|||
|
|||
|
Write
Preview
Loading…
Cancel
Save