You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
5.2 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. '''
  2. Created on Dec 11, 2013
  3. @author: Chris
  4. Collection of functions for extracting argparse related statements from the
  5. client code.
  6. '''
  7. import os
  8. import ast
  9. from itertools import chain
  10. import codegen
  11. from monkey_parser import MonkeyParser
  12. from gooey.gui.action_sorter import ActionSorter
  13. from parser_exceptions import ParserError
  14. def parse_source_file(file_name):
  15. """
  16. Parses the AST of Python file for lines containing
  17. references to the argparse module.
  18. returns the collection of ast objects found.
  19. Example client code:
  20. 1. parser = ArgumentParser(desc="My help Message")
  21. 2. parser.add_argument('filename', help="Name of the file to load")
  22. 3. parser.add_argument('-f', '--format', help='Format of output \nOptions: ['md', 'html']
  23. 4. args = parser.parse_args()
  24. Variables:
  25. * nodes Primary syntax tree object
  26. * _mainfunc_block main() method as found in the ast nodes
  27. * _try_blocks Try/except/finally blocks found in the main method
  28. * main_block The code block in which the ArgumentParser statements are located
  29. * argparse_assign_obj The assignment of the ArgumentParser (line 1 in example code)
  30. * parser_nodes Nodes which have parser references (lines 1-4 in example code)
  31. * parser_var_name The instance variable of the ArgumentParser (line 1 in example code)
  32. * ast_source The curated collection of all parser related nodes in the client code
  33. """
  34. nodes = ast.parse(_openfile(file_name))
  35. _mainfunc_block = find_main(nodes)
  36. _try_blocks = find_try_blocks(_mainfunc_block)
  37. nodes_to_search = chain([_mainfunc_block], _try_blocks)
  38. main_block = find_block_containing_argparse(nodes_to_search)
  39. argparse_assign_obj = find_assignment_objects(main_block)
  40. parser_nodes = find_parser_nodes(main_block)
  41. full_ast_source = chain(argparse_assign_obj, parser_nodes)
  42. return full_ast_source
  43. def _openfile(file_name):
  44. with open(file_name, 'rb') as f:
  45. return f.read()
  46. def find_main(nodes):
  47. code_block = _find_block(nodes, ast.FunctionDef, lambda node: node.name == 'main')
  48. if code_block != None:
  49. return code_block
  50. else:
  51. raise ParserError('Could not find main function')
  52. def find_try_blocks(nodes):
  53. return _find_blocks(nodes, [ast.TryExcept, ast.TryFinally], lambda x: x)
  54. def find_imports(nodes):
  55. return _find_blocks(nodes, ast.ImportFrom, lambda x: x.module == 'argparse')
  56. def _find_block(nodes, types, predicate):
  57. blocks = _find_blocks(nodes, types, predicate)
  58. return blocks[0] if blocks else None
  59. def _find_blocks(nodes, types, predicate):
  60. _types = types if isinstance(types, list) else [types]
  61. return [node
  62. for node in nodes.body
  63. if any([isinstance(node, _type) for _type in _types])
  64. and predicate(node)]
  65. def find_block_containing_argparse(search_locations):
  66. # Browses a collection of Nodes for the one containing the Argparse instantiation
  67. for location in search_locations:
  68. if has_argparse_assignment(location):
  69. return location
  70. raise ParserError("Could not locate AugmentParser.")
  71. def has_argparse_assignment(block):
  72. # Checks a given node for presence of an ArgumentParser instantiation
  73. argparse_assignment = _find_statement(block, has_instantiator, 'ArgumentParser')
  74. return is_found(argparse_assignment)
  75. def find_assignment_objects(ast_block):
  76. return _find_statement(ast_block, has_instantiator, 'ArgumentParser')
  77. def find_parser_nodes(ast_block):
  78. return _find_statement(ast_block, has_assignment, 'add_argument')
  79. def is_found(stmnt):
  80. return len(stmnt)
  81. def _find_statement(block, predicate, name):
  82. return [node for node in block.body
  83. if predicate(node, name)]
  84. def has_instantiator(x, name):
  85. # Checks if the astObject is one with an instantiation of the ArgParse class
  86. return has_attr(name, lambda _name: x.value.func.id == _name)
  87. def has_assignment(node, name):
  88. # Checks if the astObject contains a function with a name of name
  89. return has_attr(name, lambda _name: node.value.func.attr == _name)
  90. def has_attr(name, attr_predicate):
  91. try:
  92. return attr_predicate(name)
  93. except AttributeError as e:
  94. return False # Wrong type. Ignore.
  95. def get_assignment_name(node):
  96. # return the variable name to which ArgumentParser is assigned
  97. return node.targets[0].id
  98. def convert_to_python(ast_source):
  99. """
  100. Converts the ast objects back into human readable Python code
  101. """
  102. return map(codegen.to_source, ast_source)
  103. def extract_parser(modulepath):
  104. ast_source = parse_source_file(modulepath)
  105. if ast_source:
  106. python_code = convert_to_python(ast_source)
  107. return MonkeyParser(python_code)
  108. return None
  109. if __name__ == '__main__':
  110. filepath = os.path.join(os.path.dirname(__file__),
  111. 'mockapplications',
  112. 'example_argparse_souce_in_main.py')
  113. nodes = ast.parse(_openfile(filepath))
  114. ast_source = parse_source_file(filepath)
  115. python_code = convert_to_python(ast_source)
  116. parser = MonkeyParser(python_code)
  117. factory = ActionSorter(parser._actions)
  118. print factory._positionals