<commit_before>"""Support for making clones of projects for test isolation.
"""

import contextlib
import logging
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import virtualenv

import git

log = logging.getLogger(__name__)


@contextlib.contextmanager
def cloned_workspace(clone_config, chdir=True):
    """Create a cloned workspace and yield it.

    This creates a workspace for a with-block and cleans it up on exit. By
    default, this will also change to the workspace's `clone_dir` for the
    duration of the with-block.

    Args:
        clone_config: The execution engine configuration to use for the workspace.
        chdir: Whether to change to the workspace's `clone_dir` before entering the with-block.

    Yields: The `CloneWorkspace` instance created for the context.
    """
    workspace = ClonedWorkspace(clone_config)
    original_dir = os.getcwd()
    if chdir:
        os.chdir(workspace.clone_dir)

    try:
        yield workspace
    finally:
        os.chdir(original_dir)
        workspace.cleanup()


class ClonedWorkspace:
    """Clone a project and install it into a temporary virtual environment.

    Note that this actually *activates* the virtual environment, so don't construct one
    of these unless you want that to happen in your process.
    """

    def __init__(self, clone_config):
        self._tempdir = tempfile.TemporaryDirectory()
        log.info('New project clone in %s', self._tempdir.name)

        self._clone_dir = str(Path(self._tempdir.name) / 'repo')

        if clone_config['method'] == 'git':
            _clone_with_git(
                clone_config.get('repo-uri', '.'),
                self._clone_dir)
        elif clone_config['method'] == 'copy':
            _clone_with_copy(
                os.getcwd(),
                self._clone_dir)

        # pylint: disable=fixme
        # TODO: We should allow user to specify which version of Python to use.
        # How? The EnvBuilder could be passed a path to a python interpreter
        # which is used in the call to pip. This path would need to come from
        # the config.

        # Install into venv
        self._venv_path = Path(self._tempdir.name) / 'venv'
        log.info('Creating virtual environment in %s', self._venv_path)

        # Neither the venv nor virtualenv APIs for creating virtual environments seem to work
        # in all cases and platforms. But shelling out like this does seem to work. Strange.
        proc = subprocess.run(f'{sys.executable} -m virtualenv {self._venv_path}',
                              shell=True,
                              stderr=subprocess.STDOUT,
                              stdout=subprocess.PIPE,
                              check=True)

        log.info('Created virtual environment: %s', proc.stdout)

        _activate(self._venv_path)

        self._run_commands(clone_config.get('commands', ()))

    @property<commit_msg>Removed use of f-strings for 3.5 compat.<commit_after>"""Support for making clones of projects for test isolation.
"""

import contextlib
import logging
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import virtualenv

import git

log = logging.getLogger(__name__)


@contextlib.contextmanager
def cloned_workspace(clone_config, chdir=True):
    """Create a cloned workspace and yield it.

    This creates a workspace for a with-block and cleans it up on exit. By
    default, this will also change to the workspace's `clone_dir` for the
    duration of the with-block.

    Args:
        clone_config: The execution engine configuration to use for the workspace.
        chdir: Whether to change to the workspace's `clone_dir` before entering the with-block.

    Yields: The `CloneWorkspace` instance created for the context.
    """
    workspace = ClonedWorkspace(clone_config)
    original_dir = os.getcwd()
    if chdir:
        os.chdir(workspace.clone_dir)

    try:
        yield workspace
    finally:
        os.chdir(original_dir)
        workspace.cleanup()


class ClonedWorkspace:
    """Clone a project and install it into a temporary virtual environment.

    Note that this actually *activates* the virtual environment, so don't construct one
    of these unless you want that to happen in your process.
    """

    def __init__(self, clone_config):
        self._tempdir = tempfile.TemporaryDirectory()
        log.info('New project clone in %s', self._tempdir.name)

        self._clone_dir = str(Path(self._tempdir.name) / 'repo')

        if clone_config['method'] == 'git':
            _clone_with_git(
                clone_config.get('repo-uri', '.'),
                self._clone_dir)
        elif clone_config['method'] == 'copy':
            _clone_with_copy(
                os.getcwd(),
                self._clone_dir)

        # pylint: disable=fixme
        # TODO: We should allow user to specify which version of Python to use.
        # How? The EnvBuilder could be passed a path to a python interpreter
        # which is used in the call to pip. This path would need to come from
        # the config.

        # Install into venv
        self._venv_path = Path(self._tempdir.name) / 'venv'
        log.info('Creating virtual environment in %s', self._venv_path)
        cmd = '{} -m virtualenv {}'.format(
            sys.executable,
            self._venv_path)

        # Neither the venv nor virtualenv APIs for creating virtual environments seem to work
        # in all cases and platforms. But shelling out like this does seem to work. Strange.
        proc = subprocess.run(cmd,
                              shell=True,
                              stderr=subprocess.STDOUT,
                              stdout=subprocess.PIPE,
                              check=True)

        log.info('Created virtual environment: %s', proc.stdout)

        _activate(self._venv_path)

        self._run_commands(clone_config.get('commands', ()))

    @property
{'fraction_non_alphanumeric': 0.07853577371048252, 'fraction_numerical': 0.00033277870216306157, 'mean_word_length': 2.3922121896162527, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3796807', 'n_tokens_mistral': 1651, 'n_tokens_neox': 1594, 'n_words': 568}