<commit_before>import os
import string
from urllib.parse import urlparse
import uuid

from docker.errors import APIError, NotFound
from escapism import escape
from remoteappmanager.docker.async_docker_client import AsyncDockerClient
from remoteappmanager.docker.container import Container
from remoteappmanager.docker.docker_client_config import DockerClientConfig
from remoteappmanager.docker.image import Image
from remoteappmanager.logging.logging_mixin import LoggingMixin
from tornado import gen
from traitlets import (
    Dict,
    Int,
    Set,
    Instance,
    default)


#: Set of characters that are safe to use and should not be escaped by escapism
_CONTAINER_SAFE_CHARS = set(string.ascii_letters + string.digits + '-.')
_CONTAINER_ESCAPE_CHAR = '_'


class ContainerManager(LoggingMixin):
    #: The configuration of the docker client.
    #: Can be None. If that's the case, it will use the environment variables.
    docker_config = Instance(DockerClientConfig,
                             allow_none=True)

    #: Mapping of container_id to containers that are started by this
    #: manager (they may or may not be still running)
    containers = Dict()

    #: The asynchronous docker client.
    docker_client = Instance(AsyncDockerClient)

    #: The container (not host) port. We decided it's 8888 by default. It will
    #: be mapped to a random port on the host, so that our reverse proxy can
    #: refer to it.
    container_port = Int(8888)

    #: Keeps association of the containers for each image that this manager
    #: has started.
    _containers_for_image = Dict()

    #: Tracks if a given container is starting up.
    _start_pending = Set()

    #: Tracks if a given container is stopping down.
    _stop_pending = Set()

    @gen.coroutine
    def start_container(self, user_name, image_name, volumes=None):
        """Starts a container using the given image name.

        Parameters
        ----------
        user_name: string
            The name of the user
        image_name: string
            A string identifying the image name.
        volumes: dict
            {volume_source: {'bind': volume_target, 'mode': volume_mode}

        Return
        ------
        A container object containing information about the started container.
        """

        if image_name in self._start_pending:
            return None

        try:
            self._start_pending.add(image_name)
            result = yield self._start_container(user_name, image_name,
                                                 volumes)
        finally:
            self._start_pending.remove(image_name)

        return result

    @gen.coroutine
    def stop_and_remove_container(self, container_id):
        if container_id in self._stop_pending:
            return

        try:
            self._stop_pending.add(container_id)
            yield self._stop_and_remove_container(container_id)
        finally:
            self._stop_pending.remove(container_id)

    @gen.coroutine
    def containers_for_image(self, image_id_or_name, user_name=None):
        """Returns the currently running containers for a given image.

        If `user_name` is given, only returns containers started by the
        given user name.

        It is a coroutine because we might want to run an inquire to the docker
        service if not present.

        Parameters
        ----------
        image_id_or_name: str
            The image id or name

        Optional parameters
        -------------------
        user_name : str
            Name of the user who started the container

        Return
        ------
        A list of container objects, or an empty list if not present.
        """
        if user_name:
            user_labels = _get_container_labels(user_name)
            if user_labels:
                filters = {'label': '{0}={1}'.format(*user_labels.popitem())}
        else:
            filters = {}

        filters['ancestor'] = image_id_or_name

        containers = yield self.docker_client.containers(filters=filters)
        return [Container.from_docker_dict(container)
                for container in containers
                # Require further filtering as ancestor include grandparents
                if (container.get('Image') == image_id_or_name or
                    container.get('ImageID') == image_id_or_name)]

    @gen.coroutine
    def all_images(self):
        """Inquires all available images. Returns a list of Image objects.
        """
        image_dicts = yield self.docker_client.images(
            filters=dict(dangling=False))

        images = []
        for image_dict in image_dicts:
            image = Image.from_docker_dict(image_dict)
            images.append(image)

        return images

    @gen.coroutine
    def image(self, image_id_or_name):
        image_dict = yield self.docker_client.inspect_image(image_id_or_name)
        return Image.from_docker_dict(image_dict)
<commit_msg>Added inquiring about single image<commit_after>import os
import string
from urllib.parse import urlparse
import uuid

from docker.errors import APIError, NotFound
from escapism import escape
from remoteappmanager.docker.async_docker_client import AsyncDockerClient
from remoteappmanager.docker.container import Container
from remoteappmanager.docker.docker_client_config import DockerClientConfig
from remoteappmanager.docker.image import Image
from remoteappmanager.logging.logging_mixin import LoggingMixin
from tornado import gen
from traitlets import (
    Dict,
    Int,
    Set,
    Instance,
    default)


#: Set of characters that are safe to use and should not be escaped by escapism
_CONTAINER_SAFE_CHARS = set(string.ascii_letters + string.digits + '-.')
_CONTAINER_ESCAPE_CHAR = '_'


class ContainerManager(LoggingMixin):
    #: The configuration of the docker client.
    #: Can be None. If that's the case, it will use the environment variables.
    docker_config = Instance(DockerClientConfig,
                             allow_none=True)

    #: Mapping of container_id to containers that are started by this
    #: manager (they may or may not be still running)
    containers = Dict()

    #: The asynchronous docker client.
    docker_client = Instance(AsyncDockerClient)

    #: The container (not host) port. We decided it's 8888 by default. It will
    #: be mapped to a random port on the host, so that our reverse proxy can
    #: refer to it.
    container_port = Int(8888)

    #: Keeps association of the containers for each image that this manager
    #: has started.
    _containers_for_image = Dict()

    #: Tracks if a given container is starting up.
    _start_pending = Set()

    #: Tracks if a given container is stopping down.
    _stop_pending = Set()

    @gen.coroutine
    def start_container(self, user_name, image_name, volumes=None):
        """Starts a container using the given image name.

        Parameters
        ----------
        user_name: string
            The name of the user
        image_name: string
            A string identifying the image name.
        volumes: dict
            {volume_source: {'bind': volume_target, 'mode': volume_mode}

        Return
        ------
        A container object containing information about the started container.
        """

        if image_name in self._start_pending:
            return None

        try:
            self._start_pending.add(image_name)
            result = yield self._start_container(user_name, image_name,
                                                 volumes)
        finally:
            self._start_pending.remove(image_name)

        return result

    @gen.coroutine
    def stop_and_remove_container(self, container_id):
        if container_id in self._stop_pending:
            return

        try:
            self._stop_pending.add(container_id)
            yield self._stop_and_remove_container(container_id)
        finally:
            self._stop_pending.remove(container_id)

    @gen.coroutine
    def containers_for_image(self, image_id_or_name, user_name=None):
        """Returns the currently running containers for a given image.

        If `user_name` is given, only returns containers started by the
        given user name.

        It is a coroutine because we might want to run an inquire to the docker
        service if not present.

        Parameters
        ----------
        image_id_or_name: str
            The image id or name

        Optional parameters
        -------------------
        user_name : str
            Name of the user who started the container

        Return
        ------
        A list of container objects, or an empty list if not present.
        """
        if user_name:
            user_labels = _get_container_labels(user_name)
            if user_labels:
                filters = {'label': '{0}={1}'.format(*user_labels.popitem())}
        else:
            filters = {}

        filters['ancestor'] = image_id_or_name

        containers = yield self.docker_client.containers(filters=filters)
        return [Container.from_docker_dict(container)
                for container in containers
                # Require further filtering as ancestor include grandparents
                if (container.get('Image') == image_id_or_name or
                    container.get('ImageID') == image_id_or_name)]

    @gen.coroutine
    def all_images(self):
        """Inquires all available images. Returns a list of Image objects.
        """
        image_dicts = yield self.docker_client.images(
            filters=dict(dangling=False))

        images = []
        for image_dict in image_dicts:
            image = Image.from_docker_dict(image_dict)
            images.append(image)

        return images

    @gen.coroutine
    def image(self, image_id_or_name):
        """Returns the Image object associated to a given id
        or name. If the image is not found, it returns None."""
        try:
            image_dict = yield self.docker_client.inspect_image(
                image_id_or_name)
        except NotFound:
            return None

        return Image.from_docker_dict(image_dict)

{'fraction_non_alphanumeric': 0.07208890637293469, 'fraction_numerical': 0.001966955153422502, 'mean_word_length': 2.2760953608247423, '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': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2225465', 'n_tokens_mistral': 2763, 'n_tokens_neox': 2609, 'n_words': 866}