Source code for pynguin.assertion.mutation_analysis.controller

#  This file is part of Pynguin.
#
#  SPDX-FileCopyrightText: 2019–2026 Pynguin Contributors
#
#  SPDX-License-Identifier: MIT
#
"""Provides a controller for generating mutants."""

from __future__ import annotations

import ast
import logging
from typing import TYPE_CHECKING

from pynguin.assertion.mutation_analysis.transformer import create_module

if TYPE_CHECKING:
    import types
    from collections.abc import Generator
    from types import ModuleType

    import pynguin.assertion.mutation_analysis.mutators as mu
    from pynguin.assertion.mutation_analysis.operators.base import Mutation


_LOGGER = logging.getLogger(__name__)


[docs] class MutationController: """A controller that creates mutants.""" def __init__( self, mutant_generator: mu.Mutator, module_ast: ast.Module, module: types.ModuleType, ) -> None: """Initialize the controller. Args: mutant_generator: The mutant generator to use. module_ast: The AST of the module to mutate. module: The module to mutate. """ self._mutant_generator = mutant_generator self._module_ast = module_ast self._module = module
[docs] def create_mutant(self, mutant_ast: ast.Module) -> ModuleType: """Creates a mutant of the module. Args: mutant_ast: The mutant AST. Returns: The created mutant module. """ return create_module(mutant_ast, self._module.__name__)
[docs] def create_mutants( self, ) -> Generator[tuple[ModuleType | None, list[Mutation]]]: """Creates mutants for the module. Returns: A generator of tuples where the first entry is the mutated module or None if the mutated module cannot be created and the second part is a list of all the mutations operators applied. """ for mutations, mutant_ast in self._mutant_generator.mutate(self._module_ast, self._module): assert isinstance(mutant_ast, ast.Module) try: mutant_module = self.create_mutant(mutant_ast) except Exception as exception: # noqa: BLE001 _LOGGER.debug("Error creating mutant: %s", exception) mutant_module = None except SystemExit as exception: _LOGGER.debug("Caught SystemExit during mutant creation/execution: %s", exception) mutant_module = None yield mutant_module, mutations
[docs] def mutant_count(self) -> int: """Calculates the number of mutants that can be created. Returns: The number of mutants that can be created. """ return sum(1 for _ in self._mutant_generator.mutate(self._module_ast, self._module))