<commit_before>from copy import deepcopy
import errno
from functools import partial
import os
from subprocess import check_call
from tarfile import TarFile

import pytest

from git_archive_all import GitArchiver


def makedirs(p):
    try:
        os.makedirs(p)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise


def pytest_param(*values, **kwargs):
    try:
        return pytest.param(*values, **kwargs)
    except AttributeError:
        return values


@pytest.fixture
def git_env(tmpdir_factory):
    """
    Return ENV git configured for tests:

    1. Both system and user configs are ignored
    2. Custom git user
    3. .gitmodules file is ignored by default
    """
    e = deepcopy(os.environ)
    e['GIT_CONFIG_NOSYSTEM'] = 'true'
    e['HOME'] = str(tmpdir_factory.getbasetemp())

    with tmpdir_factory.getbasetemp().join('.gitconfig').open('w+') as f:
        f.writelines([
            '[core]\n',
            'attributesfile = {0}/.gitattributes\n'.format(tmpdir_factory.getbasetemp()),
            '[user]\n',
            'name = git-archive-all\n',
            'email = git-archive-all@example.com\n',
        ])

    with tmpdir_factory.getbasetemp().join('.gitattributes').open('w+') as f:
        f.writelines([
            '.gitmodules export-ignore'
        ])

    return e


class Record:
    def __init__(self, kind, contents, excluded=False):
        self.kind = kind
        self.contents = contents
        self.excluded = excluded

    def __getitem__(self, item):
        return self.contents[item]

    def __setitem__(self, key, value):
        self.contents[key] = value


FileRecord = partial(Record, 'file', excluded=False)
DirRecord = partial(Record, 'dir', excluded=False)
SubmoduleRecord = partial(Record, 'submodule', excluded=False)


class Repo:
    def __init__(self, path, git_env):
        self.path = os.path.abspath(path)
        self.git_env = git_env

    def init(self):
        os.mkdir(self.path)
        check_call(['git', 'init'], cwd=self.path)

    def add(self, rel_path, record):
        if record.kind == 'file':
            return self.add_file(rel_path, record.contents)
        elif record.kind == 'dir':
            return self.add_dir(rel_path, record.contents)
        elif record.kind == 'submodule':
            return self.add_submodule(rel_path, record.contents)
        else:
            raise ValueError

    def add_file(self, rel_path, contents):
        file_path = os.path.join(self.path, rel_path)

        with open(file_path, 'w') as f:
            f.write(contents)

        check_call(['git', 'add', file_path], cwd=self.path, env=self.git_env)
        return file_path

    def add_dir(self, rel_path, contents):
        dir_path = os.path.join(self.path, rel_path)
        makedirs(dir_path)

        for k, v in contents.items():
            self.add(os.path.join(dir_path, k), v)

        check_call(['git', 'add', dir_path], cwd=self.path, env=self.git_env)
        return dir_path

    def add_submodule(self, rel_path, contents):
        submodule_path = os.path.join(self.path, rel_path)
        r = Repo(submodule_path, self.git_env)
        r.init()
        r.add_dir('.', contents)
        r.commit('init')
        check_call(['git', 'submodule', 'add', submodule_path], cwd=self.path, env=self.git_env)
        return submodule_path

    def commit(self, message):
        check_call(['git', 'commit', '-m', 'init'], cwd=self.path, env=self.git_env)

    def archive(self, path):
        a = GitArchiver(main_repo_abspath=self.path)
        a.create(path)


base = {
    'app': DirRecord({
        '__init__.py': FileRecord('#Beautiful is better than ugly.'),
    }),
    'lib': SubmoduleRecord({
        '__init__.py': FileRecord('#Explicit is better than implicit.'),
        'extra': SubmoduleRecord({
            '__init__.py': FileRecord('#Simple is better than complex.'),
        })
    })
}

ignore_in_root = deepcopy(base)
ignore_in_root['.gitattributes'] = FileRecord('\ntests/__init__.py export-ignore')
ignore_in_root['tests'] = DirRecord({
    '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True)
})

ignore_in_submodule = deepcopy(base)
ignore_in_submodule['lib']['.gitattributes'] = FileRecord('\ntests/__init__.py export-ignore')
ignore_in_submodule['lib']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Flat is better than nested.', excluded=True)
})

ignore_in_nested_submodule = deepcopy(base)
ignore_in_nested_submodule['lib']['extra']['.gitattributes'] = FileRecord('\ntests/__init__.py export-ignore')
ignore_in_nested_submodule['lib']['extra']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Sparse is better than dense.', excluded=True)
})

ignore_in_submodule_from_root = deepcopy(base)
ignore_in_submodule_from_root['.gitattributes'] = FileRecord('\nlib/tests/__init__.py export-ignore')
ignore_in_submodule_from_root['lib']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Readability counts.', excluded=True)
})

ignore_in_nested_submodule_from_root = deepcopy(base)
ignore_in_nested_submodule_from_root['.gitattributes'] = FileRecord('\nlib/extra/tests/__init__.py export-ignore')
ignore_in_nested_submodule_from_root['lib']['extra']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Special cases aren\'t special enough to break the rules.', excluded=True)
})

ignore_in_nested_submodule_from_submodule = deepcopy(base)
ignore_in_nested_submodule_from_submodule['lib']['.gitattributes'] = FileRecord('\nextra/tests/__init__.py export-ignore')
ignore_in_nested_submodule_from_submodule['lib']['extra']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Although practicality beats purity.', excluded=True)
})


@pytest.mark.parametrize('contents', [
    pytest_param(base, id='No Ignore'),
    pytest_param(ignore_in_root, id='Ignore in Root'),
    pytest_param(ignore_in_submodule, id='Ignore in Submodule'),
    pytest_param(ignore_in_nested_submodule, id='Ignore in Nested Submodule'),
    pytest_param(ignore_in_submodule_from_root, id='Ignore in Submodule from Root'),
    pytest_param(ignore_in_nested_submodule_from_root, id='Ignore in Nested Submodule from Root'),
])<commit_msg>Use pytest.param directly.<commit_after>from copy import deepcopy
import errno
from functools import partial
import os
from subprocess import check_call
from tarfile import TarFile

import pytest

from git_archive_all import GitArchiver


def makedirs(p):
    try:
        os.makedirs(p)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise


@pytest.fixture
def git_env(tmpdir_factory):
    """
    Return ENV git configured for tests:

    1. Both system and user configs are ignored
    2. Custom git user
    3. .gitmodules file is ignored by default
    """
    e = deepcopy(os.environ)
    e['GIT_CONFIG_NOSYSTEM'] = 'true'
    e['HOME'] = str(tmpdir_factory.getbasetemp())

    with tmpdir_factory.getbasetemp().join('.gitconfig').open('w+') as f:
        f.writelines([
            '[core]\n',
            'attributesfile = {0}/.gitattributes\n'.format(tmpdir_factory.getbasetemp()),
            '[user]\n',
            'name = git-archive-all\n',
            'email = <EMAIL>\n',
        ])

    with tmpdir_factory.getbasetemp().join('.gitattributes').open('w+') as f:
        f.writelines([
            '.gitmodules export-ignore'
        ])

    return e


class Record:
    def __init__(self, kind, contents, excluded=False):
        self.kind = kind
        self.contents = contents
        self.excluded = excluded

    def __getitem__(self, item):
        return self.contents[item]

    def __setitem__(self, key, value):
        self.contents[key] = value


FileRecord = partial(Record, 'file', excluded=False)
DirRecord = partial(Record, 'dir', excluded=False)
SubmoduleRecord = partial(Record, 'submodule', excluded=False)


class Repo:
    def __init__(self, path, git_env):
        self.path = os.path.abspath(path)
        self.git_env = git_env

    def init(self):
        os.mkdir(self.path)
        check_call(['git', 'init'], cwd=self.path)

    def add(self, rel_path, record):
        if record.kind == 'file':
            return self.add_file(rel_path, record.contents)
        elif record.kind == 'dir':
            return self.add_dir(rel_path, record.contents)
        elif record.kind == 'submodule':
            return self.add_submodule(rel_path, record.contents)
        else:
            raise ValueError

    def add_file(self, rel_path, contents):
        file_path = os.path.join(self.path, rel_path)

        with open(file_path, 'w') as f:
            f.write(contents)

        check_call(['git', 'add', file_path], cwd=self.path, env=self.git_env)
        return file_path

    def add_dir(self, rel_path, contents):
        dir_path = os.path.join(self.path, rel_path)
        makedirs(dir_path)

        for k, v in contents.items():
            self.add(os.path.join(dir_path, k), v)

        check_call(['git', 'add', dir_path], cwd=self.path, env=self.git_env)
        return dir_path

    def add_submodule(self, rel_path, contents):
        submodule_path = os.path.join(self.path, rel_path)
        r = Repo(submodule_path, self.git_env)
        r.init()
        r.add_dir('.', contents)
        r.commit('init')
        check_call(['git', 'submodule', 'add', submodule_path], cwd=self.path, env=self.git_env)
        return submodule_path

    def commit(self, message):
        check_call(['git', 'commit', '-m', 'init'], cwd=self.path, env=self.git_env)

    def archive(self, path):
        a = GitArchiver(main_repo_abspath=self.path)
        a.create(path)


base = {
    'app': DirRecord({
        '__init__.py': FileRecord('#Beautiful is better than ugly.'),
    }),
    'lib': SubmoduleRecord({
        '__init__.py': FileRecord('#Explicit is better than implicit.'),
        'extra': SubmoduleRecord({
            '__init__.py': FileRecord('#Simple is better than complex.'),
        })
    })
}

ignore_in_root = deepcopy(base)
ignore_in_root['.gitattributes'] = FileRecord('tests/__init__.py export-ignore')
ignore_in_root['tests'] = DirRecord({
    '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True)
})

ignore_in_submodule = deepcopy(base)
ignore_in_submodule['lib']['.gitattributes'] = FileRecord('tests/__init__.py export-ignore')
ignore_in_submodule['lib']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Flat is better than nested.', excluded=True)
})

ignore_in_nested_submodule = deepcopy(base)
ignore_in_nested_submodule['lib']['extra']['.gitattributes'] = FileRecord('tests/__init__.py export-ignore')
ignore_in_nested_submodule['lib']['extra']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Sparse is better than dense.', excluded=True)
})

ignore_in_submodule_from_root = deepcopy(base)
ignore_in_submodule_from_root['.gitattributes'] = FileRecord('lib/tests/__init__.py export-ignore')
ignore_in_submodule_from_root['lib']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Readability counts.', excluded=True)
})

ignore_in_nested_submodule_from_root = deepcopy(base)
ignore_in_nested_submodule_from_root['.gitattributes'] = FileRecord('lib/extra/tests/__init__.py export-ignore')
ignore_in_nested_submodule_from_root['lib']['extra']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Special cases aren\'t special enough to break the rules.', excluded=True)
})

ignore_in_nested_submodule_from_submodule = deepcopy(base)
ignore_in_nested_submodule_from_submodule['lib']['.gitattributes'] = FileRecord('extra/tests/__init__.py export-ignore')
ignore_in_nested_submodule_from_submodule['lib']['extra']['tests'] = DirRecord({
    '__init__.py': FileRecord('#Although practicality beats purity.', excluded=True)
})


@pytest.mark.parametrize('contents', [
    pytest.param(base, id='No Ignore'),
    pytest.param(ignore_in_root, id='Ignore in Root'),
    pytest.param(ignore_in_submodule, id='Ignore in Submodule'),
    pytest.param(ignore_in_nested_submodule, id='Ignore in Nested Submodule'),
    pytest.param(ignore_in_submodule_from_root, id='Ignore in Submodule from Root'),
    pytest.param(ignore_in_nested_submodule_from_root, id='Ignore in Nested Submodule from Root'),
])
{'fraction_non_alphanumeric': 0.1369616736128136, 'fraction_numerical': 0.0006537550053117594, 'mean_word_length': 3.6426403641881637, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, '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': '2608464', 'n_tokens_mistral': 3931, 'n_tokens_neox': 3898, 'n_words': 793}