def __init__(self, test_case: tc.TestCase) -> None: """Create new execution context for the given test case.""" self._local_namespace: Dict[str, Any] = dict() self._variable_names = NamingScope() self._modules_aliases = NamingScope(prefix="module") self._ast_nodes = self._to_ast_nodes(test_case, self._variable_names, self._modules_aliases) self._global_namespace = self._prepare_global_namespace( self._modules_aliases)
def test_statement_to_ast_with_wrap(): var_names = NamingScope() module_aliases = NamingScope(prefix="module") statement_to_ast_visitor = stmt_to_ast.StatementToAstVisitor( module_aliases, var_names, True) int_stmt = MagicMock(stmt.Statement) int_stmt.value = 5 statement_to_ast_visitor.visit_int_primitive_statement(int_stmt) assert (astor.to_source(Module(body=statement_to_ast_visitor.ast_nodes)) == "try:\n var0 = 5\nexcept BaseException:\n pass\n")
def visit_default_test_case(self, test_case: dtc.DefaultTestCase) -> None: statement_visitor = stmt_to_ast.StatementToAstVisitor( self._module_aliases, NamingScope(), self._wrap_code ) for statement in test_case.statements: statement.accept(statement_visitor) self._test_case_asts.append(statement_visitor.ast_nodes)
def __init__(self, wrap_code: bool = False) -> None: """The module aliases are shared between test cases. Args: wrap_code: Whether or not exported code shall be wrapped """ self._module_aliases = NamingScope("module") self._test_case_asts: List[List[stmt]] = [] self._wrap_code = wrap_code
def __init__(self, wrap_code: bool = False) -> None: """The module aliases are shared between test cases. Args: wrap_code: Whether or not exported code shall be wrapped """ self._module_aliases = NamingScope("module") # Common modules (e.g. math) are not aliased. self._common_modules: Set[str] = set() self._test_case_asts: List[List[stmt]] = [] self._wrap_code = wrap_code
def _prepare_global_namespace( modules_aliases: NamingScope, ) -> Dict[str, ModuleType]: """Provides the required modules under the given aliases. :param modules_aliases: The module aliases :return: A dictionary of module aliases and the corresponding module """ global_namespace: Dict[str, ModuleType] = {} for required_module in modules_aliases.known_name_indices: global_namespace[modules_aliases.get_name( required_module)] = sys.modules[required_module] return global_namespace
def visit_default_test_case(self, test_case: dtc.DefaultTestCase) -> None: variables = NamingScope() statement_visitor = stmt_to_ast.StatementToAstVisitor( self._module_aliases, variables, self._wrap_code) for statement in test_case.statements: statement.accept(statement_visitor) # TODO(fk) better way. Nest visitors? assertion_visitor = ata.AssertionToAstVisitor( self._common_modules, variables) for assertion in statement.assertions: assertion.accept(assertion_visitor) statement_visitor.append_nodes(assertion_visitor.nodes) self._test_case_asts.append(statement_visitor.ast_nodes)
def create_var_name(variable_names: NamingScope, var: vr.VariableReference, load: bool) -> ast.Name: """Create a name node for the corresponding variable. Args: variable_names: the naming scope for the variables var: the variable reference load: load or store? Returns: the name node """ return ast.Name( id=variable_names.get_name(var), ctx=ast.Load() if load else ast.Store(), )
def _create_ast_imports( module_aliases: NamingScope, additional_import: Optional[str] = None) -> List[ast.stmt]: imports: List[ast.stmt] = [] if additional_import: imports.append( ast.Import( names=[ast.alias(name=additional_import, asname=None)])) for module_name in module_aliases.known_name_indices: imports.append( ast.Import(names=[ ast.alias( name=module_name, asname=module_aliases.get_name(module_name), ) ])) return imports
def _create_ast_imports( module_aliases: NamingScope, common_modules: Optional[Set[str]] = None) -> List[ast.stmt]: imports: List[ast.stmt] = [] if common_modules is not None: for module in common_modules: imports.append( ast.Import(names=[ast.alias(name=module, asname=None)])) for module_name in module_aliases.known_name_indices: imports.append( ast.Import(names=[ ast.alias( name=module_name, asname=module_aliases.get_name(module_name), ) ])) return imports
def test_naming_scope_same(): scope = NamingScope() some_object = "something" name1 = scope.get_name(some_object) name2 = scope.get_name(some_object) assert name1 == name2
def test_naming_scope_known_indices_not_empty(): scope = NamingScope() some_object = "something" scope.get_name(some_object) assert scope.known_name_indices == {some_object: 0}
def test_naming_scope_known_indices_empty(): scope = NamingScope() assert scope.known_name_indices == {}
def test_naming_scope_different(): scope = NamingScope() name1 = scope.get_name("one name") name2 = scope.get_name("another") assert name1 != name2
def statement_to_ast_visitor() -> stmt_to_ast.StatementToAstVisitor: var_names = NamingScope() module_aliases = NamingScope(prefix="module") return stmt_to_ast.StatementToAstVisitor(module_aliases, var_names)
class ExecutionContext: """Contains information required in the context of an execution. e.g. the used variables, modules and the AST representation of the statements that should be executed.""" def __init__(self) -> None: """Create new execution context.""" self._local_namespace: Dict[str, Any] = {} self._variable_names = NamingScope() self._modules_aliases = NamingScope(prefix="module") self._global_namespace: Dict[str, ModuleType] = {} @property def local_namespace(self) -> Dict[str, Any]: """The local namespace. Returns: The local namespace """ return self._local_namespace def get_variable_value(self, variable: vr.VariableReference) -> Optional[Any]: """Returns the value that is assigned to the given variable in the local namespace. Args: variable: the variable whose value we want Raises: ValueError, if the requested variable has no assigned value in this context. Returns: the assigned value. """ if variable in self._variable_names.known_name_indices: name = self._variable_names.get_name(variable) if name in self._local_namespace: return self._local_namespace.get(name) raise ValueError("Variable is not defined in this context") @property def global_namespace(self) -> Dict[str, ModuleType]: """The global namespace. Returns: The global namespace """ return self._global_namespace def executable_node_for( self, statement: stmt.Statement, ) -> ast.Module: """Transforms the given statement in an executable ast node. Args: statement: The statement that should be converted. Returns: An executable ast node. """ modules_before = len(self._modules_aliases.known_name_indices) visitor = stmt_to_ast.StatementToAstVisitor(self._modules_aliases, self._variable_names) statement.accept(visitor) if modules_before != len(self._modules_aliases.known_name_indices): # new module added # TODO(fk) cleaner solution? self._global_namespace = ExecutionContext._create_global_namespace( self._modules_aliases) assert (len(visitor.ast_nodes) == 1 ), "Expected statement to produce exactly one ast node" return ExecutionContext._wrap_node_in_module(visitor.ast_nodes[0]) @staticmethod def _wrap_node_in_module(node: ast.stmt) -> ast.Module: """Wraps the given node in a module, such that it can be executed. Args: node: The node to wrap Returns: The module wrapping the node """ ast.fix_missing_locations(node) return ast.Module(body=[node], type_ignores=[]) @staticmethod def _create_global_namespace( modules_aliases: NamingScope, ) -> Dict[str, ModuleType]: """Provides the required modules under the given aliases. Args: modules_aliases: The module aliases Returns: A dictionary of module aliases and the corresponding module """ global_namespace: Dict[str, ModuleType] = {} for required_module in modules_aliases.known_name_indices: global_namespace[modules_aliases.get_name( required_module)] = sys.modules[required_module] return global_namespace
def __init__(self) -> None: """Create new execution context.""" self._local_namespace: Dict[str, Any] = {} self._variable_names = NamingScope() self._modules_aliases = NamingScope(prefix="module") self._global_namespace: Dict[str, ModuleType] = {}
def assertion_to_ast() -> ata.AssertionToAstVisitor: scope = NamingScope() return ata.AssertionToAstVisitor(set(), scope)
def __init__(self, wrap_code: bool = False) -> None: """The module aliases are shared between test cases.""" self._module_aliases = NamingScope("module") self._test_case_asts: List[List[stmt]] = [] self._wrap_code = wrap_code