Source code for pygalgen.generator.common.source_file_parsing.local_module_parsing

"""
Module responsible for resolving assignments and constant list comprehensives
used in argument parser
"""
import ast
from typing import List, Tuple, Optional, Any, Dict
from pygalgen.common.utils import LINTER_MAGIC
from pygalgen.generator.common.source_file_parsing.parsing_commons import add_parents
import logging


[docs] class UnknownNamesRemoval(ast.NodeVisitor): """ Removes unknown names that can't be resolved and replaces them with a constant string that can be detected by linter Attributes --- unknown: Set[str] set of names that represent variables used in parser initialization, that have not been resolved yet """ def __init__(self, unknown: set[str]): self.unknown = unknown # currently able to resolve add_argument calls containing unknown names, # and list comprehension assignment def _reach_top(self, node: ast.Name) -> Tuple[ast.Call, ast.AST]: current = node parent = current.parent def _reach_add_argument(): return (isinstance(parent, ast.Call) and isinstance(parent.func, ast.Attribute) and parent.func.attr == "add_argument") def _reach_assignment_as_list_comprehension(): return (isinstance(parent, ast.Assign) and isinstance(current, ast.ListComp)) while not (_reach_add_argument() or _reach_assignment_as_list_comprehension()): current = parent parent = current.parent return parent, current def _fix_name(self, node: ast.Name, name: str): parent, current = self._reach_top(node) not_found_const = ast.Constant(value=f"{LINTER_MAGIC} Name {name}" f" could not be loaded") # if top is assignment if isinstance(parent, ast.Assign): logging.warning( f"Problem with assignment to {parent.targets[0].id}") parent.value = ast.List(elts=[not_found_const], ctx=ast.Load()) return # this name can be a part of normal args if current in parent.args: idx = parent.args.index(current) parent.args[idx] = not_found_const return # or a part of keyword args current: ast.keyword current.value = not_found_const # we dont care about class defifnitions, they should only depend # on outside things
[docs] def visit_ClassDef(self, node: ast.ClassDef) -> Any: return
[docs] def visit_Name(self, node: ast.Name) -> Any: if node.id not in self.unknown: return self._fix_name(node, node.id)
[docs] def handle_local_module_names(actions: List[ast.AST], unknown_names: set[str]) -> ast.Module: """ Function used to remove assignments and list comprehensions which can't be resolved Parameters ---------- actions : List[ast.AST] list of actions extracted so far unknown_names : set of unknown names that have to be extracted Returns ------- Python module containing assignments and list comprehensions whose values are based on constants """ module = ast.Module(body=actions, type_ignores=[]) add_parents(module) removal = UnknownNamesRemoval(unknown_names) removal.visit(module) return module