<commit_before># -*- coding: utf-8 -*-
from __future__ import unicode_literals

import logging

import sys
from requests import Timeout

from ..build.context import DockerContext
from ..dep import ImageDependentsResolver
from ..exceptions import PartialResultsError


log = logging.getLogger(__name__)


def is_repo_image(image):
    """
    Checks whether the given image has a name, i.e. is a repository image. This does not imply that it is
    assigned to an external repository.

    :param image: Image structure from the Docker Remote API.
    :type image: dict
    :return: ``False`` if the only image name and tag is <none>, or if there is no tag at all, ``True`` otherwise.
    :rtype: bool
    """
    repo_tags = image['RepoTags']
    if not repo_tags:
        return False
    return repo_tags[0] != '<none>:<none>'


def tag_check_function(tags):
    """
    Generates a function that checks whether the given image has any of the listed tags.

    :param tags: Tags to check for.
    :type tags: list[unicode | str] | set[unicode | str]
    :return: Function that returns ``True`` if any of the given tags apply to the image, ``False`` otherwise.
    :rtype: (unicode | str) -> bool
    """
    suffixes = [':{0}'.format(t) for t in tags]

    def _check_image(image):
        repo_tags = image['RepoTags']
        if not repo_tags:
            return False
        return any(r_tag.endswith(s) for s in suffixes for r_tag in repo_tags)

    return _check_image


def primary_container_name(names, default=None, strip_trailing_slash=True):
    """
    From the list of names, finds the primary name of the container. Returns the defined default value (e.g. the
    container id or ``None``) in case it cannot find any.

    :param names: List with name and aliases of the container.
    :type names: list[unicode | str]
    :param default: Default value.
    :param strip_trailing_slash: As read directly from the Docker service, every container name includes a trailing
     slash. Set this to ``False`` if it is already removed.
    :type strip_trailing_slash: bool
    :return: Primary name of the container.
    :rtype: unicode | str
    """
    if strip_trailing_slash:
        ex_names = [name[1:] for name in names if name.find('/', 2) == -1]
    else:
        ex_names = [name for name in names if name.find('/', 2) == -1]
    if ex_names:
        return ex_names[0]
    return default


class DockerUtilityMixin(object):
    def add_extra_tags(self, image_id, main_tag, extra_tags, add_latest):
        """
        Adds extra tags to an image after de-duplicating tag names.

        :param image_id: Id of the image.
        :type image_id: unicode | str
        :param main_tag: Repo / tag specification that has been used to build the image. If present, the tag will be
         removed from further arguments.
        :type main_tag: unicode | str
        :param extra_tags: Additional tags to add to the image.
        :type extra_tags: list | tuple | set | NoneType
        :param add_latest: Whether to add a ``latest`` tag to the image.
        :type add_latest: bool
        """
        repo, __, i_tag = main_tag.rpartition(':')
        tag_set = set(extra_tags or ())
        if add_latest:
            tag_set.add('latest')
        tag_set.discard(i_tag)
        added_tags = []
        if repo and tag_set:
            for t in tag_set:
                try:
                    self.tag(image_id, repo, t, force=True)
                except:
                    exc_info = sys.exc_info()
                    raise PartialResultsError(exc_info, added_tags)
                else:
                    added_tags.append(t)
        return added_tags

    def push_log(self, info, level, *args, **kwargs):
        """
        Writes logs. To be fully implemented by subclasses.
<commit_msg>Consider force tag deprecation.<commit_after># -*- coding: utf-8 -*-
from __future__ import unicode_literals

import logging

import sys

from distutils.version import StrictVersion
from requests import Timeout

from ..build.context import DockerContext
from ..dep import ImageDependentsResolver
from ..exceptions import PartialResultsError


DEPRECATED_FORCE_TAG_VERSION = StrictVersion(str('1.24'))

log = logging.getLogger(__name__)


def is_repo_image(image):
    """
    Checks whether the given image has a name, i.e. is a repository image. This does not imply that it is
    assigned to an external repository.

    :param image: Image structure from the Docker Remote API.
    :type image: dict
    :return: ``False`` if the only image name and tag is <none>, or if there is no tag at all, ``True`` otherwise.
    :rtype: bool
    """
    repo_tags = image['RepoTags']
    if not repo_tags:
        return False
    return repo_tags[0] != '<none>:<none>'


def tag_check_function(tags):
    """
    Generates a function that checks whether the given image has any of the listed tags.

    :param tags: Tags to check for.
    :type tags: list[unicode | str] | set[unicode | str]
    :return: Function that returns ``True`` if any of the given tags apply to the image, ``False`` otherwise.
    :rtype: (unicode | str) -> bool
    """
    suffixes = [':{0}'.format(t) for t in tags]

    def _check_image(image):
        repo_tags = image['RepoTags']
        if not repo_tags:
            return False
        return any(r_tag.endswith(s) for s in suffixes for r_tag in repo_tags)

    return _check_image


def primary_container_name(names, default=None, strip_trailing_slash=True):
    """
    From the list of names, finds the primary name of the container. Returns the defined default value (e.g. the
    container id or ``None``) in case it cannot find any.

    :param names: List with name and aliases of the container.
    :type names: list[unicode | str]
    :param default: Default value.
    :param strip_trailing_slash: As read directly from the Docker service, every container name includes a trailing
     slash. Set this to ``False`` if it is already removed.
    :type strip_trailing_slash: bool
    :return: Primary name of the container.
    :rtype: unicode | str
    """
    if strip_trailing_slash:
        ex_names = [name[1:] for name in names if name.find('/', 2) == -1]
    else:
        ex_names = [name for name in names if name.find('/', 2) == -1]
    if ex_names:
        return ex_names[0]
    return default


class DockerUtilityMixin(object):
    def add_extra_tags(self, image_id, main_tag, extra_tags, add_latest):
        """
        Adds extra tags to an image after de-duplicating tag names.

        :param image_id: Id of the image.
        :type image_id: unicode | str
        :param main_tag: Repo / tag specification that has been used to build the image. If present, the tag will be
         removed from further arguments.
        :type main_tag: unicode | str
        :param extra_tags: Additional tags to add to the image.
        :type extra_tags: list | tuple | set | NoneType
        :param add_latest: Whether to add a ``latest`` tag to the image.
        :type add_latest: bool
        """
        repo, __, i_tag = main_tag.rpartition(':')
        tag_set = set(extra_tags or ())
        if add_latest:
            tag_set.add('latest')
        tag_set.discard(i_tag)
        added_tags = []
        tag_kwargs = {}
        if str(self.api_version) < DEPRECATED_FORCE_TAG_VERSION:
            tag_kwargs['force'] = True
        if repo and tag_set:
            for t in tag_set:
                try:
                    self.tag(image_id, repo, t, **tag_kwargs)
                except:
                    exc_info = sys.exc_info()
                    raise PartialResultsError(exc_info, added_tags)
                else:
                    added_tags.append(t)
        return added_tags

    def push_log(self, info, level, *args, **kwargs):
        """
        Writes logs. To be fully implemented by subclasses.

{'fraction_non_alphanumeric': 0.0894350139346339, 'fraction_numerical': 0.0026602482898403852, 'mean_word_length': 2.535602328705777, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 11, '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': '2245552', 'n_tokens_mistral': 2331, 'n_tokens_neox': 2263, 'n_words': 871}