mirror of https://github.com/chriskiehl/Gooey.git
4 changed files with 0 additions and 865 deletions
Unified 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