Source code for pygalgen.generator.common.source_file_parsing.unknown_names_discovery
"""
Module containing discovery classes used to find names
(assignments to variables) that have not been extracted yet
"""
import ast
from .parsing_commons import Discovery, CustomVisitor
from typing import Tuple, List, Any, Set
import sys
import builtins
[docs]
class UnknownNamesDiscovery(CustomVisitor):
"""
Discovery class used to find names that have not been initialized yet but
are necessary for correct argument parser init
"""
def __init__(self, known_names: Set[str]):
self.known_names = known_names
self.unknown_names = set()
[docs]
def visit_Name(self, node: ast.Name) -> Any:
if node.id not in self.known_names:
self.unknown_names.add(node.id)
# def visit_For(self, node: ast.For) -> Any:
# for item in node.body:
# self.generic_visit(item)
[docs]
def visit_ListComp(self, node: ast.ListComp) -> Any:
for comprehension in node.generators:
if isinstance(comprehension.target, ast.Name):
self.known_names.add(comprehension.target.id)
self.generic_visit(node)
[docs]
def report_findings(self) -> Tuple:
return self.unknown_names,
[docs]
class UnknownNameInit(CustomVisitor):
"""
Class used to initialize unknown names
"""
def __init__(self, unknown_names: Set[str]):
self.unknown_names = unknown_names
self.class_definitions = []
self.variable_definitions = []
self.new_known_names = set()
# assignment of variables
[docs]
def visit_Assign(self, node: ast.Assign) -> Any:
target, = node.targets
if isinstance(target, ast.Name) and target.id in self.unknown_names:
self.variable_definitions.append(node)
self.new_known_names.add(target.id)
# if members of class are used, class definition has to be a
# part of actions
[docs]
def visit_ClassDef(self, node: ast.ClassDef) -> Any:
if node.name in self.unknown_names:
self.class_definitions.append(node)
self.new_known_names.add(node.name)
[docs]
def report_findings(self) -> Tuple:
return self.variable_definitions, self.class_definitions, \
self.new_known_names
def _insert_into_actions(actions: List[ast.AST], assignments: List[ast.Assign],
class_defs: List[ast.ClassDef]):
def find_end_of_imports():
index = 0
for item in actions:
if not (isinstance(item, ast.Import) or
isinstance(item, ast.ImportFrom)):
return index
index += 1
return index
end_of_imports = find_end_of_imports()
if assignments:
actions.insert(end_of_imports, *assignments)
if class_defs:
actions.insert(end_of_imports, *class_defs)
[docs]
def initialize_variables_in_module(original_module: ast.Module,
parser_name: str,
sections: Set[str],
actions: List[ast.AST],
imported_names: Set[str]) -> \
Tuple[List[ast.AST], Set[str]]:
"""
Function used to initialize variables that have constant values
Parameters
----------
original_module : ast.Module
AST of the original source file
parser_name : str
default name of the parser
sections : Set[str]
set of section names
actions : List[ast.AST]
list of actions extracted so far
imported_names : Set[str]
list of names imported from modules
Returns
-------
List containing newly extracted actions and new unknown names
"""
builtin_names = [e for e in builtins.__dict__]
lib_modules = sys.stdlib_module_names
# this is a set of all known names, basically the things that are already
# known and don't have to be added to the list of actions
known_names = {parser_name, *sections,
*builtin_names, *lib_modules, *imported_names}
while True:
unknown_names_discovery = UnknownNamesDiscovery(known_names)
extracted_module = ast.Module(body=actions, type_ignores=[])
unknown_names, = unknown_names_discovery.visit_and_report(
extracted_module)
# after unknown names initialization is complete, new known_names set is
# created and new actions are added to the action list
unknown_names_loader = UnknownNameInit(unknown_names)
new_vars, new_classes, new_known_names = \
unknown_names_loader.visit_and_report(original_module)
_insert_into_actions(actions, new_vars, new_classes)
known_names = known_names.union(new_known_names)
if len(new_known_names) == 0:
break
return actions, unknown_names