Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
pantsbuild.pants / cache / resolver.py
Size: Mime:
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import json
import logging
from abc import ABC, abstractmethod

import requests

from pants.base.validation import assert_list

logger = logging.getLogger(__name__)


class Resolver(ABC):
    """An abstract class base for resolving service urls.

    :API: public
    """

    class ResolverError(Exception):
        """Indicate an error resolving service urls.

        :API: public
        """

    @abstractmethod
    def resolve(self, resolve_from):
        """Query resolve_from for a list of service urls.

        :param resolve_from: The discovery endpoint that may be on different protocols.
        :return: A non-empty list of URLs. URL is the same format as remote artifact cache.
        :rtype: list of strings
        :raises :class:`Resolver.ResolverError` if there is an error resolving service urls.
        """


class NoopResolver(Resolver):
    """A resolver that always yields nothing.

    :API: public
    """

    def resolve(self, resolve_from):
        """
    :API: public
    """
        return []


class ResponseParser:
    """Resolver response parser utility class.

    :API: public
    """

    class ResponseParserError(Exception):
        """Indicates an error parsing response from resolver.

        :API: public
        """

    def __init__(self, format="json_map", encoding="utf-8", index="hostlist"):
        """
        :API: public
        """
        self.format = format
        self.encoding = encoding
        self.index = index

    def parse(self, content):
        """Parse raw response content for a list of remote artifact cache URLs.

        :API: public
        """
        if self.format == "json_map":
            try:
                return assert_list(json.loads(content.decode(self.encoding))[self.index])
            except (KeyError, UnicodeDecodeError, ValueError) as e:
                raise self.ResponseParserError(
                    "Error while parsing response content: {0}".format(str(e))
                )

        # Should never get here.
        raise ValueError('Unknown content format: "{}"'.format(self.format))


class RESTfulResolver(Resolver):
    """Query a resolver on RESTful interface.

    :API: public
    """

    def __init__(self, timeout, tries, response_parser=None):
        """
    :API: public

    :param float timeout: Timeout for GET in seconds.
    :param int tries: Max number of retries. See docstring on `requests.adapters.HTTPAdapter`
                      for details.
    :param response_parser: Parser to extract the resolved URLS from response.
    """
        self._timeout = timeout
        self._tries = tries
        self._response_parser = response_parser or ResponseParser()

    def _safe_get_content(self, session, resolve_from):
        try:
            resp = session.get(resolve_from, timeout=self._timeout)
            if resp.status_code == requests.codes.ok:
                return resp.content
            raise self.ResolverError("Error status_code={0}".format(resp.status_code))
        except requests.RequestException:
            raise self.ResolverError("Request error from {0}".format(resolve_from))

    def resolve(self, resolve_from):
        """
        :API: public
        """
        session = requests.Session()
        session.mount(resolve_from, requests.adapters.HTTPAdapter(max_retries=self._tries))
        content = self._safe_get_content(session, resolve_from)
        try:
            parsed_urls = self._response_parser.parse(content)
            if len(parsed_urls) > 0:
                return parsed_urls
            raise self.ResolverError("Empty result received from {0}".format(resolve_from))
        except ResponseParser.ResponseParserError as e:
            raise self.ResolverError("Error parsing response: {0}".format(str(e)))