Ejemplo n.º 1
0
def write_mutant_cache_file(mutant: Mutant) -> None:
    """Create the cache file for the mutant on disk in __pycache__.

    Existing target cache files are removed to ensure clean overwrites.

    Reference: https://github.com/python/cpython/blob/master/Lib/py_compile.py#L157

    Args:
        mutant: the mutant definition to create

    Returns:
        None, creates the cache file on disk.
    """
    check_cache_invalidation_mode()

    bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(  # type: ignore
        mutant.mutant_code, mutant.source_stats["mtime"],
        mutant.source_stats["size"])

    remove_existing_cache_files(mutant.src_file)

    create_cache_dirs(mutant.cfile)

    LOGGER.debug("Writing mutant cache file: %s", mutant.cfile)
    importlib._bootstrap_external._write_atomic(mutant.cfile, bytecode,
                                                mutant.mode)  # type: ignore
Ejemplo n.º 2
0
def cli_main() -> None:
    """Entry point to run CLI args and execute main function."""
    # Run a quick check at the beginning in case of later OS errors.
    check_cache_invalidation_mode()
    args = cli_args(sys.argv[1:])
    main(args)
Ejemplo n.º 3
0
def test_check_cache_invalidation_mode_ok(monkeypatch):
    """Returned value should always ben TIMESTAMP until hashlib support."""
    monkeypatch.delenv("SOURCE_DATE_EPOCH", raising=False)

    mode = check_cache_invalidation_mode()
    assert mode == PycInvalidationMode.TIMESTAMP
Ejemplo n.º 4
0
def test_check_cache_invalidation_mode_error(monkeypatch):
    """Ensure OS error is raised when SOURCE_DATE_EPOCH is set."""
    monkeypatch.setenv("SOURCE_DATE_EPOCH", "testvalue")

    with pytest.raises(EnvironmentError):
        check_cache_invalidation_mode()
Ejemplo n.º 5
0
def create_mutation_run_parallelcache_trial(
        genome: Genome, target_idx: LocIndex, mutation_op: Any,
        test_cmds: List[str], max_runtime: float) -> MutantTrialResult:
    """Similar to run.create_mutation_run_trial() but using the parallel cache directory settings.

    This function requires Python 3.8 and does not run with Python 3.7. Importantly, it has the
    identical signature to run.create_mutation_run_trial() and is substituted in the
    run.mutation_sample_dispatch().

    Args:
        genome: the genome to mutate
        target_idx: the mutation location
        mutation_op: the mutation operation
        test_cmds: the test commands to execute with the mutated code
        max_runtime: timeout for the subprocess trial

    Returns:
        MutantTrialResult

    Raises:
        EnvironmentError: if Python version is less than 3.8
    """

    if sys.version_info < (3, 8):
        raise EnvironmentError(
            "Python 3.8 is required to use PYTHONPYCACHEPREFIX.")

    # Note in coverage reports this shows as untested code due to the subprocess dispatching
    # the 'slow' tests in `test_run.py` cover this.
    cache.check_cache_invalidation_mode()

    # create the mutant without writing the cache
    mutant = genome.mutate(target_idx, mutation_op, write_cache=False)

    # set up parallel cache structure
    parallel_cache = Path.cwd() / PARALLEL_PYCACHE_DIR / uuid.uuid4().hex
    resolved_source_parts = genome.source_file.resolve().parent.parts[
        1:]  # type: ignore
    parallel_cfile = parallel_cache.joinpath(
        *resolved_source_parts) / mutant.cfile.name

    bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(  # type: ignore
        mutant.mutant_code, mutant.source_stats["mtime"],
        mutant.source_stats["size"])

    LOGGER.debug("Writing parallel mutant cache file: %s", parallel_cfile)
    cache.create_cache_dirs(parallel_cfile)
    importlib._bootstrap_external._write_atomic(  # type: ignore
        parallel_cfile,
        bytecode,
        mutant.mode,
    )

    copy_env = os.environ.copy()
    copy_env["PYTHONPYCACHEPREFIX"] = str(parallel_cache)
    try:
        mutant_trial = subprocess.run(
            test_cmds,
            env=copy_env,
            capture_output=capture_output(LOGGER.getEffectiveLevel()),
            timeout=max_runtime + MULTI_PROC_TIMEOUT_BUFFER,
        )
        return_code = mutant_trial.returncode

    except subprocess.TimeoutExpired:
        return_code = 3

    LOGGER.debug("Removing parallel cache file: %s", parallel_cache.parts[-1])
    shutil.rmtree(parallel_cache)

    return MutantTrialResult(
        mutant=MutantReport(src_file=mutant.src_file,
                            src_idx=mutant.src_idx,
                            mutation=mutant.mutation),
        return_code=return_code,
    )